From d83023a0eaf4f97713e86b700d415a21e4e5add2 Mon Sep 17 00:00:00 2001 From: Kmods SIG Date: Jan 13 2022 23:40:15 +0000 Subject: Switch to EL kernel source and versioning --- diff --git a/.gitignore b/.gitignore index 5e161a9..72f7423 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/ntfs3-5.15.tar.xz +SOURCES/ntfs3-4.18.0-0.el8.tar.xz diff --git a/.kmod-ntfs3.metadata b/.kmod-ntfs3.metadata index b6eca0e..007e868 100644 --- a/.kmod-ntfs3.metadata +++ b/.kmod-ntfs3.metadata @@ -1 +1 @@ -3c1f905dfc71ea509dd445cf6a4f2c5e608da0c6 SOURCES/ntfs3-5.15.tar.xz +f2ac7452c365b3269b936867a161660e20197579 SOURCES/ntfs3-4.18.0-0.el8.tar.xz diff --git a/SOURCES/0001-fs-ntfs3-Add-headers-and-misc-files.patch b/SOURCES/0001-fs-ntfs3-Add-headers-and-misc-files.patch new file mode 100644 index 0000000..7bffc75 --- /dev/null +++ b/SOURCES/0001-fs-ntfs3-Add-headers-and-misc-files.patch @@ -0,0 +1,2545 @@ +From 4534a70b7056fd4b9a1c6db5a4ce3c98546b291e Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:29 +0300 +Subject: [Backport 4534a70b7056] src: Add headers and misc files + +This adds headers and misc files + +Signed-off-by: Konstantin Komarov +--- + src/debug.h | 64 +++ + src/ntfs.h | 1238 ++++++++++++++++++++++++++++++++++++++++++++ + src/ntfs_fs.h | 1092 ++++++++++++++++++++++++++++++++++++++ + src/upcase.c | 105 ++++ + 4 files changed, 2499 insertions(+) + create mode 100644 src/debug.h + create mode 100644 src/ntfs.h + create mode 100644 src/ntfs_fs.h + create mode 100644 src/upcase.c + +diff --git a/src/debug.h b/src/debug.h +new file mode 100644 +index 0000000000000000000000000000000000000000..dfaa4c79dc6d86677b0b3de5c98a54a23ef637d6 +--- /dev/null ++++ b/src/debug.h +@@ -0,0 +1,64 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * useful functions for debuging ++ */ ++ ++// clang-format off ++#ifndef Add2Ptr ++#define Add2Ptr(P, I) ((void *)((u8 *)(P) + (I))) ++#define PtrOffset(B, O) ((size_t)((size_t)(O) - (size_t)(B))) ++#endif ++ ++#define QuadAlign(n) (((n) + 7u) & (~7u)) ++#define IsQuadAligned(n) (!((size_t)(n)&7u)) ++#define Quad2Align(n) (((n) + 15u) & (~15u)) ++#define IsQuad2Aligned(n) (!((size_t)(n)&15u)) ++#define Quad4Align(n) (((n) + 31u) & (~31u)) ++#define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1))) ++#define DwordAlign(n) (((n) + 3u) & (~3u)) ++#define IsDwordAligned(n) (!((size_t)(n)&3u)) ++#define WordAlign(n) (((n) + 1u) & (~1u)) ++#define IsWordAligned(n) (!((size_t)(n)&1u)) ++ ++#ifdef CONFIG_PRINTK ++__printf(2, 3) ++void ntfs_printk(const struct super_block *sb, const char *fmt, ...); ++__printf(2, 3) ++void ntfs_inode_printk(struct inode *inode, const char *fmt, ...); ++#else ++static inline __printf(2, 3) ++void ntfs_printk(const struct super_block *sb, const char *fmt, ...) ++{ ++} ++ ++static inline __printf(2, 3) ++void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) ++{ ++} ++#endif ++ ++/* ++ * Logging macros ( thanks Joe Perches for implementation ) ++ */ ++ ++#define ntfs_err(sb, fmt, ...) ntfs_printk(sb, KERN_ERR fmt, ##__VA_ARGS__) ++#define ntfs_warn(sb, fmt, ...) ntfs_printk(sb, KERN_WARNING fmt, ##__VA_ARGS__) ++#define ntfs_info(sb, fmt, ...) ntfs_printk(sb, KERN_INFO fmt, ##__VA_ARGS__) ++#define ntfs_notice(sb, fmt, ...) \ ++ ntfs_printk(sb, KERN_NOTICE fmt, ##__VA_ARGS__) ++ ++#define ntfs_inode_err(inode, fmt, ...) \ ++ ntfs_inode_printk(inode, KERN_ERR fmt, ##__VA_ARGS__) ++#define ntfs_inode_warn(inode, fmt, ...) \ ++ ntfs_inode_printk(inode, KERN_WARNING fmt, ##__VA_ARGS__) ++ ++#define ntfs_malloc(s) kmalloc(s, GFP_NOFS) ++#define ntfs_zalloc(s) kzalloc(s, GFP_NOFS) ++#define ntfs_vmalloc(s) kvmalloc(s, GFP_KERNEL) ++#define ntfs_free(p) kfree(p) ++#define ntfs_vfree(p) kvfree(p) ++#define ntfs_memdup(src, len) kmemdup(src, len, GFP_NOFS) ++// clang-format on +diff --git a/src/ntfs.h b/src/ntfs.h +new file mode 100644 +index 0000000000000000000000000000000000000000..40398e6c39c9383de1f917f6ef29d4d0238df7d7 +--- /dev/null ++++ b/src/ntfs.h +@@ -0,0 +1,1238 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * on-disk ntfs structs ++ */ ++ ++// clang-format off ++ ++/* TODO: ++ * - Check 4K mft record and 512 bytes cluster ++ */ ++ ++/* ++ * Activate this define to use binary search in indexes ++ */ ++#define NTFS3_INDEX_BINARY_SEARCH ++ ++/* ++ * Check each run for marked clusters ++ */ ++#define NTFS3_CHECK_FREE_CLST ++ ++#define NTFS_NAME_LEN 255 ++ ++/* ++ * ntfs.sys used 500 maximum links ++ * on-disk struct allows up to 0xffff ++ */ ++#define NTFS_LINK_MAX 0x400 ++//#define NTFS_LINK_MAX 0xffff ++ ++/* ++ * Activate to use 64 bit clusters instead of 32 bits in ntfs.sys ++ * Logical and virtual cluster number ++ * If needed, may be redefined to use 64 bit value ++ */ ++//#define CONFIG_NTFS3_64BIT_CLUSTER ++ ++#define NTFS_LZNT_MAX_CLUSTER 4096 ++#define NTFS_LZNT_CUNIT 4 ++#define NTFS_LZNT_CLUSTERS (1u<low) | ((u64)le16_to_cpu(ref->high) << 32); ++#else ++ return le32_to_cpu(ref->low); ++#endif ++} ++ ++struct NTFS_BOOT { ++ u8 jump_code[3]; // 0x00: Jump to boot code ++ u8 system_id[8]; // 0x03: System ID, equals "NTFS " ++ ++ // NOTE: this member is not aligned(!) ++ // bytes_per_sector[0] must be 0 ++ // bytes_per_sector[1] must be multiplied by 256 ++ u8 bytes_per_sector[2]; // 0x0B: Bytes per sector ++ ++ u8 sectors_per_clusters;// 0x0D: Sectors per cluster ++ u8 unused1[7]; ++ u8 media_type; // 0x15: Media type (0xF8 - harddisk) ++ u8 unused2[2]; ++ __le16 sct_per_track; // 0x18: number of sectors per track ++ __le16 heads; // 0x1A: number of heads per cylinder ++ __le32 hidden_sectors; // 0x1C: number of 'hidden' sectors ++ u8 unused3[4]; ++ u8 bios_drive_num; // 0x24: BIOS drive number =0x80 ++ u8 unused4; ++ u8 signature_ex; // 0x26: Extended BOOT signature =0x80 ++ u8 unused5; ++ __le64 sectors_per_volume;// 0x28: size of volume in sectors ++ __le64 mft_clst; // 0x30: first cluster of $MFT ++ __le64 mft2_clst; // 0x38: first cluster of $MFTMirr ++ s8 record_size; // 0x40: size of MFT record in clusters(sectors) ++ u8 unused6[3]; ++ s8 index_size; // 0x44: size of INDX record in clusters(sectors) ++ u8 unused7[3]; ++ __le64 serial_num; // 0x48: Volume serial number ++ __le32 check_sum; // 0x50: Simple additive checksum of all ++ // of the u32's which precede the 'check_sum' ++ ++ u8 boot_code[0x200 - 0x50 - 2 - 4]; // 0x54: ++ u8 boot_magic[2]; // 0x1FE: Boot signature =0x55 + 0xAA ++}; ++ ++static_assert(sizeof(struct NTFS_BOOT) == 0x200); ++ ++enum NTFS_SIGNATURE { ++ NTFS_FILE_SIGNATURE = cpu_to_le32(0x454C4946), // 'FILE' ++ NTFS_INDX_SIGNATURE = cpu_to_le32(0x58444E49), // 'INDX' ++ NTFS_CHKD_SIGNATURE = cpu_to_le32(0x444B4843), // 'CHKD' ++ NTFS_RSTR_SIGNATURE = cpu_to_le32(0x52545352), // 'RSTR' ++ NTFS_RCRD_SIGNATURE = cpu_to_le32(0x44524352), // 'RCRD' ++ NTFS_BAAD_SIGNATURE = cpu_to_le32(0x44414142), // 'BAAD' ++ NTFS_HOLE_SIGNATURE = cpu_to_le32(0x454C4F48), // 'HOLE' ++ NTFS_FFFF_SIGNATURE = cpu_to_le32(0xffffffff), ++}; ++ ++static_assert(sizeof(enum NTFS_SIGNATURE) == 4); ++ ++/* MFT Record header structure */ ++struct NTFS_RECORD_HEADER { ++ /* Record magic number, equals 'FILE'/'INDX'/'RSTR'/'RCRD' */ ++ enum NTFS_SIGNATURE sign; // 0x00: ++ __le16 fix_off; // 0x04: ++ __le16 fix_num; // 0x06: ++ __le64 lsn; // 0x08: Log file sequence number ++}; ++ ++static_assert(sizeof(struct NTFS_RECORD_HEADER) == 0x10); ++ ++static inline int is_baad(const struct NTFS_RECORD_HEADER *hdr) ++{ ++ return hdr->sign == NTFS_BAAD_SIGNATURE; ++} ++ ++/* Possible bits in struct MFT_REC.flags */ ++enum RECORD_FLAG { ++ RECORD_FLAG_IN_USE = cpu_to_le16(0x0001), ++ RECORD_FLAG_DIR = cpu_to_le16(0x0002), ++ RECORD_FLAG_SYSTEM = cpu_to_le16(0x0004), ++ RECORD_FLAG_UNKNOWN = cpu_to_le16(0x0008), ++}; ++ ++/* MFT Record structure */ ++struct MFT_REC { ++ struct NTFS_RECORD_HEADER rhdr; // 'FILE' ++ ++ __le16 seq; // 0x10: Sequence number for this record ++ __le16 hard_links; // 0x12: The number of hard links to record ++ __le16 attr_off; // 0x14: Offset to attributes ++ __le16 flags; // 0x16: See RECORD_FLAG ++ __le32 used; // 0x18: The size of used part ++ __le32 total; // 0x1C: Total record size ++ ++ struct MFT_REF parent_ref; // 0x20: Parent MFT record ++ __le16 next_attr_id; // 0x28: The next attribute Id ++ ++ __le16 res; // 0x2A: High part of mft record? ++ __le32 mft_record; // 0x2C: Current mft record number ++ __le16 fixups[]; // 0x30: ++}; ++ ++#define MFTRECORD_FIXUP_OFFSET_1 offsetof(struct MFT_REC, res) ++#define MFTRECORD_FIXUP_OFFSET_3 offsetof(struct MFT_REC, fixups) ++ ++static_assert(MFTRECORD_FIXUP_OFFSET_1 == 0x2A); ++static_assert(MFTRECORD_FIXUP_OFFSET_3 == 0x30); ++ ++static inline bool is_rec_base(const struct MFT_REC *rec) ++{ ++ const struct MFT_REF *r = &rec->parent_ref; ++ ++ return !r->low && !r->high && !r->seq; ++} ++ ++static inline bool is_mft_rec5(const struct MFT_REC *rec) ++{ ++ return le16_to_cpu(rec->rhdr.fix_off) >= ++ offsetof(struct MFT_REC, fixups); ++} ++ ++static inline bool is_rec_inuse(const struct MFT_REC *rec) ++{ ++ return rec->flags & RECORD_FLAG_IN_USE; ++} ++ ++static inline bool clear_rec_inuse(struct MFT_REC *rec) ++{ ++ return rec->flags &= ~RECORD_FLAG_IN_USE; ++} ++ ++/* Possible values of ATTR_RESIDENT.flags */ ++#define RESIDENT_FLAG_INDEXED 0x01 ++ ++struct ATTR_RESIDENT { ++ __le32 data_size; // 0x10: The size of data ++ __le16 data_off; // 0x14: Offset to data ++ u8 flags; // 0x16: resident flags ( 1 - indexed ) ++ u8 res; // 0x17: ++}; // sizeof() = 0x18 ++ ++struct ATTR_NONRESIDENT { ++ __le64 svcn; // 0x10: Starting VCN of this segment ++ __le64 evcn; // 0x18: End VCN of this segment ++ __le16 run_off; // 0x20: Offset to packed runs ++ // Unit of Compression size for this stream, expressed ++ // as a log of the cluster size. ++ // ++ // 0 means file is not compressed ++ // 1, 2, 3, and 4 are potentially legal values if the ++ // stream is compressed, however the implementation ++ // may only choose to use 4, or possibly 3. Note ++ // that 4 means cluster size time 16. If convenient ++ // the implementation may wish to accept a ++ // reasonable range of legal values here (1-5?), ++ // even if the implementation only generates ++ // a smaller set of values itself. ++ u8 c_unit; // 0x22 ++ u8 res1[5]; // 0x23: ++ __le64 alloc_size; // 0x28: The allocated size of attribute in bytes ++ // (multiple of cluster size) ++ __le64 data_size; // 0x30: The size of attribute in bytes <= alloc_size ++ __le64 valid_size; // 0x38: The size of valid part in bytes <= data_size ++ __le64 total_size; // 0x40: The sum of the allocated clusters for a file ++ // (present only for the first segment (0 == vcn) ++ // of compressed attribute) ++ ++}; // sizeof()=0x40 or 0x48 (if compressed) ++ ++/* Possible values of ATTRIB.flags: */ ++#define ATTR_FLAG_COMPRESSED cpu_to_le16(0x0001) ++#define ATTR_FLAG_COMPRESSED_MASK cpu_to_le16(0x00FF) ++#define ATTR_FLAG_ENCRYPTED cpu_to_le16(0x4000) ++#define ATTR_FLAG_SPARSED cpu_to_le16(0x8000) ++ ++struct ATTRIB { ++ enum ATTR_TYPE type; // 0x00: The type of this attribute ++ __le32 size; // 0x04: The size of this attribute ++ u8 non_res; // 0x08: Is this attribute non-resident ? ++ u8 name_len; // 0x09: This attribute name length ++ __le16 name_off; // 0x0A: Offset to the attribute name ++ __le16 flags; // 0x0C: See ATTR_FLAG_XXX ++ __le16 id; // 0x0E: unique id (per record) ++ ++ union { ++ struct ATTR_RESIDENT res; // 0x10 ++ struct ATTR_NONRESIDENT nres; // 0x10 ++ }; ++}; ++ ++/* Define attribute sizes */ ++#define SIZEOF_RESIDENT 0x18 ++#define SIZEOF_NONRESIDENT_EX 0x48 ++#define SIZEOF_NONRESIDENT 0x40 ++ ++#define SIZEOF_RESIDENT_LE cpu_to_le16(0x18) ++#define SIZEOF_NONRESIDENT_EX_LE cpu_to_le16(0x48) ++#define SIZEOF_NONRESIDENT_LE cpu_to_le16(0x40) ++ ++static inline u64 attr_ondisk_size(const struct ATTRIB *attr) ++{ ++ return attr->non_res ? ((attr->flags & ++ (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) ? ++ le64_to_cpu(attr->nres.total_size) : ++ le64_to_cpu(attr->nres.alloc_size)) : ++ QuadAlign(le32_to_cpu(attr->res.data_size)); ++} ++ ++static inline u64 attr_size(const struct ATTRIB *attr) ++{ ++ return attr->non_res ? le64_to_cpu(attr->nres.data_size) : ++ le32_to_cpu(attr->res.data_size); ++} ++ ++static inline bool is_attr_encrypted(const struct ATTRIB *attr) ++{ ++ return attr->flags & ATTR_FLAG_ENCRYPTED; ++} ++ ++static inline bool is_attr_sparsed(const struct ATTRIB *attr) ++{ ++ return attr->flags & ATTR_FLAG_SPARSED; ++} ++ ++static inline bool is_attr_compressed(const struct ATTRIB *attr) ++{ ++ return attr->flags & ATTR_FLAG_COMPRESSED; ++} ++ ++static inline bool is_attr_ext(const struct ATTRIB *attr) ++{ ++ return attr->flags & (ATTR_FLAG_SPARSED | ATTR_FLAG_COMPRESSED); ++} ++ ++static inline bool is_attr_indexed(const struct ATTRIB *attr) ++{ ++ return !attr->non_res && (attr->res.flags & RESIDENT_FLAG_INDEXED); ++} ++ ++static inline __le16 const *attr_name(const struct ATTRIB *attr) ++{ ++ return Add2Ptr(attr, le16_to_cpu(attr->name_off)); ++} ++ ++static inline u64 attr_svcn(const struct ATTRIB *attr) ++{ ++ return attr->non_res ? le64_to_cpu(attr->nres.svcn) : 0; ++} ++ ++/* the size of resident attribute by its resident size */ ++#define BYTES_PER_RESIDENT(b) (0x18 + (b)) ++ ++static_assert(sizeof(struct ATTRIB) == 0x48); ++static_assert(sizeof(((struct ATTRIB *)NULL)->res) == 0x08); ++static_assert(sizeof(((struct ATTRIB *)NULL)->nres) == 0x38); ++ ++static inline void *resident_data_ex(const struct ATTRIB *attr, u32 datasize) ++{ ++ u32 asize, rsize; ++ u16 off; ++ ++ if (attr->non_res) ++ return NULL; ++ ++ asize = le32_to_cpu(attr->size); ++ off = le16_to_cpu(attr->res.data_off); ++ ++ if (asize < datasize + off) ++ return NULL; ++ ++ rsize = le32_to_cpu(attr->res.data_size); ++ if (rsize < datasize) ++ return NULL; ++ ++ return Add2Ptr(attr, off); ++} ++ ++static inline void *resident_data(const struct ATTRIB *attr) ++{ ++ return Add2Ptr(attr, le16_to_cpu(attr->res.data_off)); ++} ++ ++static inline void *attr_run(const struct ATTRIB *attr) ++{ ++ return Add2Ptr(attr, le16_to_cpu(attr->nres.run_off)); ++} ++ ++/* Standard information attribute (0x10) */ ++struct ATTR_STD_INFO { ++ __le64 cr_time; // 0x00: File creation file ++ __le64 m_time; // 0x08: File modification time ++ __le64 c_time; // 0x10: Last time any attribute was modified ++ __le64 a_time; // 0x18: File last access time ++ enum FILE_ATTRIBUTE fa; // 0x20: Standard DOS attributes & more ++ __le32 max_ver_num; // 0x24: Maximum Number of Versions ++ __le32 ver_num; // 0x28: Version Number ++ __le32 class_id; // 0x2C: Class Id from bidirectional Class Id index ++}; ++ ++static_assert(sizeof(struct ATTR_STD_INFO) == 0x30); ++ ++#define SECURITY_ID_INVALID 0x00000000 ++#define SECURITY_ID_FIRST 0x00000100 ++ ++struct ATTR_STD_INFO5 { ++ __le64 cr_time; // 0x00: File creation file ++ __le64 m_time; // 0x08: File modification time ++ __le64 c_time; // 0x10: Last time any attribute was modified ++ __le64 a_time; // 0x18: File last access time ++ enum FILE_ATTRIBUTE fa; // 0x20: Standard DOS attributes & more ++ __le32 max_ver_num; // 0x24: Maximum Number of Versions ++ __le32 ver_num; // 0x28: Version Number ++ __le32 class_id; // 0x2C: Class Id from bidirectional Class Id index ++ ++ __le32 owner_id; // 0x30: Owner Id of the user owning the file. ++ __le32 security_id; // 0x34: The Security Id is a key in the $SII Index and $SDS ++ __le64 quota_charge; // 0x38: ++ __le64 usn; // 0x40: Last Update Sequence Number of the file. This is a direct ++ // index into the file $UsnJrnl. If zero, the USN Journal is ++ // disabled. ++}; ++ ++static_assert(sizeof(struct ATTR_STD_INFO5) == 0x48); ++ ++/* attribute list entry structure (0x20) */ ++struct ATTR_LIST_ENTRY { ++ enum ATTR_TYPE type; // 0x00: The type of attribute ++ __le16 size; // 0x04: The size of this record ++ u8 name_len; // 0x06: The length of attribute name ++ u8 name_off; // 0x07: The offset to attribute name ++ __le64 vcn; // 0x08: Starting VCN of this attribute ++ struct MFT_REF ref; // 0x10: MFT record number with attribute ++ __le16 id; // 0x18: struct ATTRIB ID ++ __le16 name[3]; // 0x1A: Just to align. To get real name can use bNameOffset ++ ++}; // sizeof(0x20) ++ ++static_assert(sizeof(struct ATTR_LIST_ENTRY) == 0x20); ++ ++static inline u32 le_size(u8 name_len) ++{ ++ return QuadAlign(offsetof(struct ATTR_LIST_ENTRY, name) + ++ name_len * sizeof(short)); ++} ++ ++/* returns 0 if 'attr' has the same type and name */ ++static inline int le_cmp(const struct ATTR_LIST_ENTRY *le, ++ const struct ATTRIB *attr) ++{ ++ return le->type != attr->type || le->name_len != attr->name_len || ++ (!le->name_len && ++ memcmp(Add2Ptr(le, le->name_off), ++ Add2Ptr(attr, le16_to_cpu(attr->name_off)), ++ le->name_len * sizeof(short))); ++} ++ ++static inline __le16 const *le_name(const struct ATTR_LIST_ENTRY *le) ++{ ++ return Add2Ptr(le, le->name_off); ++} ++ ++/* File name types (the field type in struct ATTR_FILE_NAME ) */ ++#define FILE_NAME_POSIX 0 ++#define FILE_NAME_UNICODE 1 ++#define FILE_NAME_DOS 2 ++#define FILE_NAME_UNICODE_AND_DOS (FILE_NAME_DOS | FILE_NAME_UNICODE) ++ ++/* Filename attribute structure (0x30) */ ++struct NTFS_DUP_INFO { ++ __le64 cr_time; // 0x00: File creation file ++ __le64 m_time; // 0x08: File modification time ++ __le64 c_time; // 0x10: Last time any attribute was modified ++ __le64 a_time; // 0x18: File last access time ++ __le64 alloc_size; // 0x20: Data attribute allocated size, multiple of cluster size ++ __le64 data_size; // 0x28: Data attribute size <= Dataalloc_size ++ enum FILE_ATTRIBUTE fa; // 0x30: Standard DOS attributes & more ++ __le16 ea_size; // 0x34: Packed EAs ++ __le16 reparse; // 0x36: Used by Reparse ++ ++}; // 0x38 ++ ++struct ATTR_FILE_NAME { ++ struct MFT_REF home; // 0x00: MFT record for directory ++ struct NTFS_DUP_INFO dup;// 0x08 ++ u8 name_len; // 0x40: File name length in words ++ u8 type; // 0x41: File name type ++ __le16 name[]; // 0x42: File name ++}; ++ ++static_assert(sizeof(((struct ATTR_FILE_NAME *)NULL)->dup) == 0x38); ++static_assert(offsetof(struct ATTR_FILE_NAME, name) == 0x42); ++#define SIZEOF_ATTRIBUTE_FILENAME 0x44 ++#define SIZEOF_ATTRIBUTE_FILENAME_MAX (0x42 + 255 * 2) ++ ++static inline struct ATTRIB *attr_from_name(struct ATTR_FILE_NAME *fname) ++{ ++ return (struct ATTRIB *)((char *)fname - SIZEOF_RESIDENT); ++} ++ ++static inline u16 fname_full_size(const struct ATTR_FILE_NAME *fname) ++{ ++ // don't return struct_size(fname, name, fname->name_len); ++ return offsetof(struct ATTR_FILE_NAME, name) + ++ fname->name_len * sizeof(short); ++} ++ ++static inline u8 paired_name(u8 type) ++{ ++ if (type == FILE_NAME_UNICODE) ++ return FILE_NAME_DOS; ++ if (type == FILE_NAME_DOS) ++ return FILE_NAME_UNICODE; ++ return FILE_NAME_POSIX; ++} ++ ++/* Index entry defines ( the field flags in NtfsDirEntry ) */ ++#define NTFS_IE_HAS_SUBNODES cpu_to_le16(1) ++#define NTFS_IE_LAST cpu_to_le16(2) ++ ++/* Directory entry structure */ ++struct NTFS_DE { ++ union { ++ struct MFT_REF ref; // 0x00: MFT record number with this file ++ struct { ++ __le16 data_off; // 0x00: ++ __le16 data_size; // 0x02: ++ __le32 res; // 0x04: must be 0 ++ } view; ++ }; ++ __le16 size; // 0x08: The size of this entry ++ __le16 key_size; // 0x0A: The size of File name length in bytes + 0x42 ++ __le16 flags; // 0x0C: Entry flags: NTFS_IE_XXX ++ __le16 res; // 0x0E: ++ ++ // Here any indexed attribute can be placed ++ // One of them is: ++ // struct ATTR_FILE_NAME AttrFileName; ++ // ++ ++ // The last 8 bytes of this structure contains ++ // the VBN of subnode ++ // !!! Note !!! ++ // This field is presented only if (flags & NTFS_IE_HAS_SUBNODES) ++ // __le64 vbn; ++}; ++ ++static_assert(sizeof(struct NTFS_DE) == 0x10); ++ ++static inline void de_set_vbn_le(struct NTFS_DE *e, __le64 vcn) ++{ ++ __le64 *v = Add2Ptr(e, le16_to_cpu(e->size) - sizeof(__le64)); ++ ++ *v = vcn; ++} ++ ++static inline void de_set_vbn(struct NTFS_DE *e, CLST vcn) ++{ ++ __le64 *v = Add2Ptr(e, le16_to_cpu(e->size) - sizeof(__le64)); ++ ++ *v = cpu_to_le64(vcn); ++} ++ ++static inline __le64 de_get_vbn_le(const struct NTFS_DE *e) ++{ ++ return *(__le64 *)Add2Ptr(e, le16_to_cpu(e->size) - sizeof(__le64)); ++} ++ ++static inline CLST de_get_vbn(const struct NTFS_DE *e) ++{ ++ __le64 *v = Add2Ptr(e, le16_to_cpu(e->size) - sizeof(__le64)); ++ ++ return le64_to_cpu(*v); ++} ++ ++static inline struct NTFS_DE *de_get_next(const struct NTFS_DE *e) ++{ ++ return Add2Ptr(e, le16_to_cpu(e->size)); ++} ++ ++static inline struct ATTR_FILE_NAME *de_get_fname(const struct NTFS_DE *e) ++{ ++ return le16_to_cpu(e->key_size) >= SIZEOF_ATTRIBUTE_FILENAME ? ++ Add2Ptr(e, sizeof(struct NTFS_DE)) : ++ NULL; ++} ++ ++static inline bool de_is_last(const struct NTFS_DE *e) ++{ ++ return e->flags & NTFS_IE_LAST; ++} ++ ++static inline bool de_has_vcn(const struct NTFS_DE *e) ++{ ++ return e->flags & NTFS_IE_HAS_SUBNODES; ++} ++ ++static inline bool de_has_vcn_ex(const struct NTFS_DE *e) ++{ ++ return (e->flags & NTFS_IE_HAS_SUBNODES) && ++ (u64)(-1) != *((u64 *)Add2Ptr(e, le16_to_cpu(e->size) - ++ sizeof(__le64))); ++} ++ ++#define MAX_BYTES_PER_NAME_ENTRY \ ++ QuadAlign(sizeof(struct NTFS_DE) + \ ++ offsetof(struct ATTR_FILE_NAME, name) + \ ++ NTFS_NAME_LEN * sizeof(short)) ++ ++struct INDEX_HDR { ++ __le32 de_off; // 0x00: The offset from the start of this structure ++ // to the first NTFS_DE ++ __le32 used; // 0x04: The size of this structure plus all ++ // entries (quad-word aligned) ++ __le32 total; // 0x08: The allocated size of for this structure plus all entries ++ u8 flags; // 0x0C: 0x00 = Small directory, 0x01 = Large directory ++ u8 res[3]; ++ ++ // ++ // de_off + used <= total ++ // ++}; ++ ++static_assert(sizeof(struct INDEX_HDR) == 0x10); ++ ++static inline struct NTFS_DE *hdr_first_de(const struct INDEX_HDR *hdr) ++{ ++ u32 de_off = le32_to_cpu(hdr->de_off); ++ u32 used = le32_to_cpu(hdr->used); ++ struct NTFS_DE *e = Add2Ptr(hdr, de_off); ++ u16 esize; ++ ++ if (de_off >= used || de_off >= le32_to_cpu(hdr->total)) ++ return NULL; ++ ++ esize = le16_to_cpu(e->size); ++ if (esize < sizeof(struct NTFS_DE) || de_off + esize > used) ++ return NULL; ++ ++ return e; ++} ++ ++static inline struct NTFS_DE *hdr_next_de(const struct INDEX_HDR *hdr, ++ const struct NTFS_DE *e) ++{ ++ size_t off = PtrOffset(hdr, e); ++ u32 used = le32_to_cpu(hdr->used); ++ u16 esize; ++ ++ if (off >= used) ++ return NULL; ++ ++ esize = le16_to_cpu(e->size); ++ ++ if (esize < sizeof(struct NTFS_DE) || ++ off + esize + sizeof(struct NTFS_DE) > used) ++ return NULL; ++ ++ return Add2Ptr(e, esize); ++} ++ ++static inline bool hdr_has_subnode(const struct INDEX_HDR *hdr) ++{ ++ return hdr->flags & 1; ++} ++ ++struct INDEX_BUFFER { ++ struct NTFS_RECORD_HEADER rhdr; // 'INDX' ++ __le64 vbn; // 0x10: vcn if index >= cluster or vsn id index < cluster ++ struct INDEX_HDR ihdr; // 0x18: ++}; ++ ++static_assert(sizeof(struct INDEX_BUFFER) == 0x28); ++ ++static inline bool ib_is_empty(const struct INDEX_BUFFER *ib) ++{ ++ const struct NTFS_DE *first = hdr_first_de(&ib->ihdr); ++ ++ return !first || de_is_last(first); ++} ++ ++static inline bool ib_is_leaf(const struct INDEX_BUFFER *ib) ++{ ++ return !(ib->ihdr.flags & 1); ++} ++ ++/* Index root structure ( 0x90 ) */ ++enum COLLATION_RULE { ++ NTFS_COLLATION_TYPE_BINARY = cpu_to_le32(0), ++ // $I30 ++ NTFS_COLLATION_TYPE_FILENAME = cpu_to_le32(0x01), ++ // $SII of $Secure and $Q of Quota ++ NTFS_COLLATION_TYPE_UINT = cpu_to_le32(0x10), ++ // $O of Quota ++ NTFS_COLLATION_TYPE_SID = cpu_to_le32(0x11), ++ // $SDH of $Secure ++ NTFS_COLLATION_TYPE_SECURITY_HASH = cpu_to_le32(0x12), ++ // $O of ObjId and "$R" for Reparse ++ NTFS_COLLATION_TYPE_UINTS = cpu_to_le32(0x13) ++}; ++ ++static_assert(sizeof(enum COLLATION_RULE) == 4); ++ ++// ++struct INDEX_ROOT { ++ enum ATTR_TYPE type; // 0x00: The type of attribute to index on ++ enum COLLATION_RULE rule; // 0x04: The rule ++ __le32 index_block_size;// 0x08: The size of index record ++ u8 index_block_clst; // 0x0C: The number of clusters or sectors per index ++ u8 res[3]; ++ struct INDEX_HDR ihdr; // 0x10: ++}; ++ ++static_assert(sizeof(struct INDEX_ROOT) == 0x20); ++static_assert(offsetof(struct INDEX_ROOT, ihdr) == 0x10); ++ ++#define VOLUME_FLAG_DIRTY cpu_to_le16(0x0001) ++#define VOLUME_FLAG_RESIZE_LOG_FILE cpu_to_le16(0x0002) ++ ++struct VOLUME_INFO { ++ __le64 res1; // 0x00 ++ u8 major_ver; // 0x08: NTFS major version number (before .) ++ u8 minor_ver; // 0x09: NTFS minor version number (after .) ++ __le16 flags; // 0x0A: Volume flags, see VOLUME_FLAG_XXX ++ ++}; // sizeof=0xC ++ ++#define SIZEOF_ATTRIBUTE_VOLUME_INFO 0xc ++ ++#define NTFS_LABEL_MAX_LENGTH (0x100 / sizeof(short)) ++#define NTFS_ATTR_INDEXABLE cpu_to_le32(0x00000002) ++#define NTFS_ATTR_DUPALLOWED cpu_to_le32(0x00000004) ++#define NTFS_ATTR_MUST_BE_INDEXED cpu_to_le32(0x00000010) ++#define NTFS_ATTR_MUST_BE_NAMED cpu_to_le32(0x00000020) ++#define NTFS_ATTR_MUST_BE_RESIDENT cpu_to_le32(0x00000040) ++#define NTFS_ATTR_LOG_ALWAYS cpu_to_le32(0x00000080) ++ ++/* $AttrDef file entry */ ++struct ATTR_DEF_ENTRY { ++ __le16 name[0x40]; // 0x00: Attr name ++ enum ATTR_TYPE type; // 0x80: struct ATTRIB type ++ __le32 res; // 0x84: ++ enum COLLATION_RULE rule; // 0x88: ++ __le32 flags; // 0x8C: NTFS_ATTR_XXX (see above) ++ __le64 min_sz; // 0x90: Minimum attribute data size ++ __le64 max_sz; // 0x98: Maximum attribute data size ++}; ++ ++static_assert(sizeof(struct ATTR_DEF_ENTRY) == 0xa0); ++ ++/* Object ID (0x40) */ ++struct OBJECT_ID { ++ struct GUID ObjId; // 0x00: Unique Id assigned to file ++ struct GUID BirthVolumeId;// 0x10: Birth Volume Id is the Object Id of the Volume on ++ // which the Object Id was allocated. It never changes ++ struct GUID BirthObjectId; // 0x20: Birth Object Id is the first Object Id that was ++ // ever assigned to this MFT Record. I.e. If the Object Id ++ // is changed for some reason, this field will reflect the ++ // original value of the Object Id. ++ struct GUID DomainId; // 0x30: Domain Id is currently unused but it is intended to be ++ // used in a network environment where the local machine is ++ // part of a Windows 2000 Domain. This may be used in a Windows ++ // 2000 Advanced Server managed domain. ++}; ++ ++static_assert(sizeof(struct OBJECT_ID) == 0x40); ++ ++/* O Directory entry structure ( rule = 0x13 ) */ ++struct NTFS_DE_O { ++ struct NTFS_DE de; ++ struct GUID ObjId; // 0x10: Unique Id assigned to file ++ struct MFT_REF ref; // 0x20: MFT record number with this file ++ struct GUID BirthVolumeId; // 0x28: Birth Volume Id is the Object Id of the Volume on ++ // which the Object Id was allocated. It never changes ++ struct GUID BirthObjectId; // 0x38: Birth Object Id is the first Object Id that was ++ // ever assigned to this MFT Record. I.e. If the Object Id ++ // is changed for some reason, this field will reflect the ++ // original value of the Object Id. ++ // This field is valid if data_size == 0x48 ++ struct GUID BirthDomainId; // 0x48: Domain Id is currently unused but it is intended ++ // to be used in a network environment where the local ++ // machine is part of a Windows 2000 Domain. This may be ++ // used in a Windows 2000 Advanced Server managed domain. ++}; ++ ++static_assert(sizeof(struct NTFS_DE_O) == 0x58); ++ ++#define NTFS_OBJECT_ENTRY_DATA_SIZE1 \ ++ 0x38 // struct NTFS_DE_O.BirthDomainId is not used ++#define NTFS_OBJECT_ENTRY_DATA_SIZE2 \ ++ 0x48 // struct NTFS_DE_O.BirthDomainId is used ++ ++/* Q Directory entry structure ( rule = 0x11 ) */ ++struct NTFS_DE_Q { ++ struct NTFS_DE de; ++ __le32 owner_id; // 0x10: Unique Id assigned to file ++ __le32 Version; // 0x14: 0x02 ++ __le32 flags2; // 0x18: Quota flags, see above ++ __le64 BytesUsed; // 0x1C: ++ __le64 ChangeTime; // 0x24: ++ __le64 WarningLimit; // 0x28: ++ __le64 HardLimit; // 0x34: ++ __le64 ExceededTime; // 0x3C: ++ ++ // SID is placed here ++}; // sizeof() = 0x44 ++ ++#define SIZEOF_NTFS_DE_Q 0x44 ++ ++#define SecurityDescriptorsBlockSize 0x40000 // 256K ++#define SecurityDescriptorMaxSize 0x20000 // 128K ++#define Log2OfSecurityDescriptorsBlockSize 18 ++ ++struct SECURITY_KEY { ++ __le32 hash; // Hash value for descriptor ++ __le32 sec_id; // Security Id (guaranteed unique) ++}; ++ ++/* Security descriptors (the content of $Secure::SDS data stream) */ ++struct SECURITY_HDR { ++ struct SECURITY_KEY key; // 0x00: Security Key ++ __le64 off; // 0x08: Offset of this entry in the file ++ __le32 size; // 0x10: Size of this entry, 8 byte aligned ++ // ++ // Security descriptor itself is placed here ++ // Total size is 16 byte aligned ++ // ++} __packed; ++ ++#define SIZEOF_SECURITY_HDR 0x14 ++ ++/* SII Directory entry structure */ ++struct NTFS_DE_SII { ++ struct NTFS_DE de; ++ __le32 sec_id; // 0x10: Key: sizeof(security_id) = wKeySize ++ struct SECURITY_HDR sec_hdr; // 0x14: ++} __packed; ++ ++#define SIZEOF_SII_DIRENTRY 0x28 ++ ++/* SDH Directory entry structure */ ++struct NTFS_DE_SDH { ++ struct NTFS_DE de; ++ struct SECURITY_KEY key; // 0x10: Key ++ struct SECURITY_HDR sec_hdr; // 0x18: Data ++ __le16 magic[2]; // 0x2C: 0x00490049 "I I" ++}; ++ ++#define SIZEOF_SDH_DIRENTRY 0x30 ++ ++struct REPARSE_KEY { ++ __le32 ReparseTag; // 0x00: Reparse Tag ++ struct MFT_REF ref; // 0x04: MFT record number with this file ++}; // sizeof() = 0x0C ++ ++static_assert(offsetof(struct REPARSE_KEY, ref) == 0x04); ++#define SIZEOF_REPARSE_KEY 0x0C ++ ++/* Reparse Directory entry structure */ ++struct NTFS_DE_R { ++ struct NTFS_DE de; ++ struct REPARSE_KEY key; // 0x10: Reparse Key ++ u32 zero; // 0x1c ++}; // sizeof() = 0x20 ++ ++static_assert(sizeof(struct NTFS_DE_R) == 0x20); ++ ++/* CompressReparseBuffer.WofVersion */ ++#define WOF_CURRENT_VERSION cpu_to_le32(1) ++/* CompressReparseBuffer.WofProvider */ ++#define WOF_PROVIDER_WIM cpu_to_le32(1) ++/* CompressReparseBuffer.WofProvider */ ++#define WOF_PROVIDER_SYSTEM cpu_to_le32(2) ++/* CompressReparseBuffer.ProviderVer */ ++#define WOF_PROVIDER_CURRENT_VERSION cpu_to_le32(1) ++ ++#define WOF_COMPRESSION_XPRESS4K cpu_to_le32(0) // 4k ++#define WOF_COMPRESSION_LZX32K cpu_to_le32(1) // 32k ++#define WOF_COMPRESSION_XPRESS8K cpu_to_le32(2) // 8k ++#define WOF_COMPRESSION_XPRESS16K cpu_to_le32(3) // 16k ++ ++/* ++ * ATTR_REPARSE (0xC0) ++ * ++ * The reparse struct GUID structure is used by all 3rd party layered drivers to ++ * store data in a reparse point. For non-Microsoft tags, The struct GUID field ++ * cannot be GUID_NULL. ++ * The constraints on reparse tags are defined below. ++ * Microsoft tags can also be used with this format of the reparse point buffer. ++ */ ++struct REPARSE_POINT { ++ __le32 ReparseTag; // 0x00: ++ __le16 ReparseDataLength;// 0x04: ++ __le16 Reserved; ++ ++ struct GUID Guid; // 0x08: ++ ++ // ++ // Here GenericReparseBuffer is placed ++ // ++}; ++ ++static_assert(sizeof(struct REPARSE_POINT) == 0x18); ++ ++// ++// Maximum allowed size of the reparse data. ++// ++#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) ++ ++// ++// The value of the following constant needs to satisfy the following ++// conditions: ++// (1) Be at least as large as the largest of the reserved tags. ++// (2) Be strictly smaller than all the tags in use. ++// ++#define IO_REPARSE_TAG_RESERVED_RANGE 1 ++ ++// ++// The reparse tags are a ULONG. The 32 bits are laid out as follows: ++// ++// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 ++// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ++// +-+-+-+-+-----------------------+-------------------------------+ ++// |M|R|N|R| Reserved bits | Reparse Tag Value | ++// +-+-+-+-+-----------------------+-------------------------------+ ++// ++// M is the Microsoft bit. When set to 1, it denotes a tag owned by Microsoft. ++// All ISVs must use a tag with a 0 in this position. ++// Note: If a Microsoft tag is used by non-Microsoft software, the ++// behavior is not defined. ++// ++// R is reserved. Must be zero for non-Microsoft tags. ++// ++// N is name surrogate. When set to 1, the file represents another named ++// entity in the system. ++// ++// The M and N bits are OR-able. ++// The following macros check for the M and N bit values: ++// ++ ++// ++// Macro to determine whether a reparse point tag corresponds to a tag ++// owned by Microsoft. ++// ++#define IsReparseTagMicrosoft(_tag) (((_tag)&IO_REPARSE_TAG_MICROSOFT)) ++ ++// ++// Macro to determine whether a reparse point tag is a name surrogate ++// ++#define IsReparseTagNameSurrogate(_tag) (((_tag)&IO_REPARSE_TAG_NAME_SURROGATE)) ++ ++// ++// The following constant represents the bits that are valid to use in ++// reparse tags. ++// ++#define IO_REPARSE_TAG_VALID_VALUES 0xF000FFFF ++ ++// ++// Macro to determine whether a reparse tag is a valid tag. ++// ++#define IsReparseTagValid(_tag) \ ++ (!((_tag) & ~IO_REPARSE_TAG_VALID_VALUES) && \ ++ ((_tag) > IO_REPARSE_TAG_RESERVED_RANGE)) ++ ++// ++// Microsoft tags for reparse points. ++// ++ ++enum IO_REPARSE_TAG { ++ IO_REPARSE_TAG_SYMBOLIC_LINK = cpu_to_le32(0), ++ IO_REPARSE_TAG_NAME_SURROGATE = cpu_to_le32(0x20000000), ++ IO_REPARSE_TAG_MICROSOFT = cpu_to_le32(0x80000000), ++ IO_REPARSE_TAG_MOUNT_POINT = cpu_to_le32(0xA0000003), ++ IO_REPARSE_TAG_SYMLINK = cpu_to_le32(0xA000000C), ++ IO_REPARSE_TAG_HSM = cpu_to_le32(0xC0000004), ++ IO_REPARSE_TAG_SIS = cpu_to_le32(0x80000007), ++ IO_REPARSE_TAG_DEDUP = cpu_to_le32(0x80000013), ++ IO_REPARSE_TAG_COMPRESS = cpu_to_le32(0x80000017), ++ ++ // ++ // The reparse tag 0x80000008 is reserved for Microsoft internal use ++ // (may be published in the future) ++ // ++ ++ // ++ // Microsoft reparse tag reserved for DFS ++ // ++ IO_REPARSE_TAG_DFS = cpu_to_le32(0x8000000A), ++ ++ // ++ // Microsoft reparse tag reserved for the file system filter manager ++ // ++ IO_REPARSE_TAG_FILTER_MANAGER = cpu_to_le32(0x8000000B), ++ ++ // ++ // Non-Microsoft tags for reparse points ++ // ++ ++ // ++ // Tag allocated to CONGRUENT, May 2000. Used by IFSTEST ++ // ++ IO_REPARSE_TAG_IFSTEST_CONGRUENT = cpu_to_le32(0x00000009), ++ ++ // ++ // Tag allocated to ARKIVIO ++ // ++ IO_REPARSE_TAG_ARKIVIO = cpu_to_le32(0x0000000C), ++ ++ // ++ // Tag allocated to SOLUTIONSOFT ++ // ++ IO_REPARSE_TAG_SOLUTIONSOFT = cpu_to_le32(0x2000000D), ++ ++ // ++ // Tag allocated to COMMVAULT ++ // ++ IO_REPARSE_TAG_COMMVAULT = cpu_to_le32(0x0000000E), ++ ++ // OneDrive?? ++ IO_REPARSE_TAG_CLOUD = cpu_to_le32(0x9000001A), ++ IO_REPARSE_TAG_CLOUD_1 = cpu_to_le32(0x9000101A), ++ IO_REPARSE_TAG_CLOUD_2 = cpu_to_le32(0x9000201A), ++ IO_REPARSE_TAG_CLOUD_3 = cpu_to_le32(0x9000301A), ++ IO_REPARSE_TAG_CLOUD_4 = cpu_to_le32(0x9000401A), ++ IO_REPARSE_TAG_CLOUD_5 = cpu_to_le32(0x9000501A), ++ IO_REPARSE_TAG_CLOUD_6 = cpu_to_le32(0x9000601A), ++ IO_REPARSE_TAG_CLOUD_7 = cpu_to_le32(0x9000701A), ++ IO_REPARSE_TAG_CLOUD_8 = cpu_to_le32(0x9000801A), ++ IO_REPARSE_TAG_CLOUD_9 = cpu_to_le32(0x9000901A), ++ IO_REPARSE_TAG_CLOUD_A = cpu_to_le32(0x9000A01A), ++ IO_REPARSE_TAG_CLOUD_B = cpu_to_le32(0x9000B01A), ++ IO_REPARSE_TAG_CLOUD_C = cpu_to_le32(0x9000C01A), ++ IO_REPARSE_TAG_CLOUD_D = cpu_to_le32(0x9000D01A), ++ IO_REPARSE_TAG_CLOUD_E = cpu_to_le32(0x9000E01A), ++ IO_REPARSE_TAG_CLOUD_F = cpu_to_le32(0x9000F01A), ++ ++}; ++ ++#define SYMLINK_FLAG_RELATIVE 1 ++ ++/* Microsoft reparse buffer. (see DDK for details) */ ++struct REPARSE_DATA_BUFFER { ++ __le32 ReparseTag; // 0x00: ++ __le16 ReparseDataLength; // 0x04: ++ __le16 Reserved; ++ ++ union { ++ // If ReparseTag == 0xA0000003 (IO_REPARSE_TAG_MOUNT_POINT) ++ struct { ++ __le16 SubstituteNameOffset; // 0x08 ++ __le16 SubstituteNameLength; // 0x0A ++ __le16 PrintNameOffset; // 0x0C ++ __le16 PrintNameLength; // 0x0E ++ __le16 PathBuffer[]; // 0x10 ++ } MountPointReparseBuffer; ++ ++ // If ReparseTag == 0xA000000C (IO_REPARSE_TAG_SYMLINK) ++ // https://msdn.microsoft.com/en-us/library/cc232006.aspx ++ struct { ++ __le16 SubstituteNameOffset; // 0x08 ++ __le16 SubstituteNameLength; // 0x0A ++ __le16 PrintNameOffset; // 0x0C ++ __le16 PrintNameLength; // 0x0E ++ // 0-absolute path 1- relative path, SYMLINK_FLAG_RELATIVE ++ __le32 Flags; // 0x10 ++ __le16 PathBuffer[]; // 0x14 ++ } SymbolicLinkReparseBuffer; ++ ++ // If ReparseTag == 0x80000017U ++ struct { ++ __le32 WofVersion; // 0x08 == 1 ++ /* 1 - WIM backing provider ("WIMBoot"), ++ * 2 - System compressed file provider ++ */ ++ __le32 WofProvider; // 0x0C ++ __le32 ProviderVer; // 0x10: == 1 WOF_FILE_PROVIDER_CURRENT_VERSION == 1 ++ __le32 CompressionFormat; // 0x14: 0, 1, 2, 3. See WOF_COMPRESSION_XXX ++ } CompressReparseBuffer; ++ ++ struct { ++ u8 DataBuffer[1]; // 0x08 ++ } GenericReparseBuffer; ++ }; ++}; ++ ++/* ATTR_EA_INFO (0xD0) */ ++ ++#define FILE_NEED_EA 0x80 // See ntifs.h ++/* FILE_NEED_EA, indicates that the file to which the EA belongs cannot be ++ * interpreted without understanding the associated extended attributes. ++ */ ++struct EA_INFO { ++ __le16 size_pack; // 0x00: Size of buffer to hold in packed form ++ __le16 count; // 0x02: Count of EA's with FILE_NEED_EA bit set ++ __le32 size; // 0x04: Size of buffer to hold in unpacked form ++}; ++ ++static_assert(sizeof(struct EA_INFO) == 8); ++ ++/* ATTR_EA (0xE0) */ ++struct EA_FULL { ++ __le32 size; // 0x00: (not in packed) ++ u8 flags; // 0x04 ++ u8 name_len; // 0x05 ++ __le16 elength; // 0x06 ++ u8 name[]; // 0x08 ++}; ++ ++static_assert(offsetof(struct EA_FULL, name) == 8); ++ ++#define ACL_REVISION 2 ++#define ACL_REVISION_DS 4 ++ ++#define SE_SELF_RELATIVE cpu_to_le16(0x8000) ++ ++struct SECURITY_DESCRIPTOR_RELATIVE { ++ u8 Revision; ++ u8 Sbz1; ++ __le16 Control; ++ __le32 Owner; ++ __le32 Group; ++ __le32 Sacl; ++ __le32 Dacl; ++}; ++static_assert(sizeof(struct SECURITY_DESCRIPTOR_RELATIVE) == 0x14); ++ ++struct ACE_HEADER { ++ u8 AceType; ++ u8 AceFlags; ++ __le16 AceSize; ++}; ++static_assert(sizeof(struct ACE_HEADER) == 4); ++ ++struct ACL { ++ u8 AclRevision; ++ u8 Sbz1; ++ __le16 AclSize; ++ __le16 AceCount; ++ __le16 Sbz2; ++}; ++static_assert(sizeof(struct ACL) == 8); ++ ++struct SID { ++ u8 Revision; ++ u8 SubAuthorityCount; ++ u8 IdentifierAuthority[6]; ++ __le32 SubAuthority[]; ++}; ++static_assert(offsetof(struct SID, SubAuthority) == 8); ++ ++// clang-format on +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +new file mode 100644 +index 0000000000000000000000000000000000000000..0c3ac89c3115da8131a66281c1c3c7da112d07ff +--- /dev/null ++++ b/src/ntfs_fs.h +@@ -0,0 +1,1092 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++// clang-format off ++#define MINUS_ONE_T ((size_t)(-1)) ++/* Biggest MFT / smallest cluster */ ++#define MAXIMUM_BYTES_PER_MFT 4096 ++#define NTFS_BLOCKS_PER_MFT_RECORD (MAXIMUM_BYTES_PER_MFT / 512) ++ ++#define MAXIMUM_BYTES_PER_INDEX 4096 ++#define NTFS_BLOCKS_PER_INODE (MAXIMUM_BYTES_PER_INDEX / 512) ++ ++/* ntfs specific error code when fixup failed*/ ++#define E_NTFS_FIXUP 555 ++/* ntfs specific error code about resident->nonresident*/ ++#define E_NTFS_NONRESIDENT 556 ++/* ntfs specific error code about punch hole*/ ++#define E_NTFS_NOTALIGNED 557 ++ ++ ++/* sbi->flags */ ++#define NTFS_FLAGS_NODISCARD 0x00000001 ++/* Set when LogFile is replaying */ ++#define NTFS_FLAGS_LOG_REPLAYING 0x00000008 ++/* Set when we changed first MFT's which copy must be updated in $MftMirr */ ++#define NTFS_FLAGS_MFTMIRR 0x00001000 ++#define NTFS_FLAGS_NEED_REPLAY 0x04000000 ++ ++ ++/* ni->ni_flags */ ++/* ++ * Data attribute is external compressed (lzx/xpress) ++ * 1 - WOF_COMPRESSION_XPRESS4K ++ * 2 - WOF_COMPRESSION_XPRESS8K ++ * 3 - WOF_COMPRESSION_XPRESS16K ++ * 4 - WOF_COMPRESSION_LZX32K ++ */ ++#define NI_FLAG_COMPRESSED_MASK 0x0000000f ++/* Data attribute is deduplicated */ ++#define NI_FLAG_DEDUPLICATED 0x00000010 ++#define NI_FLAG_EA 0x00000020 ++#define NI_FLAG_DIR 0x00000040 ++#define NI_FLAG_RESIDENT 0x00000080 ++#define NI_FLAG_UPDATE_PARENT 0x00000100 ++// clang-format on ++ ++struct ntfs_mount_options { ++ struct nls_table *nls; ++ ++ kuid_t fs_uid; ++ kgid_t fs_gid; ++ u16 fs_fmask_inv; ++ u16 fs_dmask_inv; ++ ++ unsigned uid : 1, /* uid was set */ ++ gid : 1, /* gid was set */ ++ fmask : 1, /* fmask was set */ ++ dmask : 1, /*dmask was set*/ ++ sys_immutable : 1, /* immutable system files */ ++ discard : 1, /* issue discard requests on deletions */ ++ sparse : 1, /*create sparse files*/ ++ showmeta : 1, /*show meta files*/ ++ nohidden : 1, /*do not show hidden files*/ ++ force : 1, /*rw mount dirty volume*/ ++ no_acs_rules : 1, /*exclude acs rules*/ ++ prealloc : 1 /*preallocate space when file is growing*/ ++ ; ++}; ++ ++/* special value to unpack and deallocate*/ ++#define RUN_DEALLOCATE ((struct runs_tree *)(size_t)1) ++ ++/* TODO: use rb tree instead of array */ ++struct runs_tree { ++ struct ntfs_run *runs; ++ size_t count; // Currently used size a ntfs_run storage. ++ size_t allocated; // Currently allocated ntfs_run storage size. ++}; ++ ++struct ntfs_buffers { ++ /* Biggest MFT / smallest cluster = 4096 / 512 = 8 */ ++ /* Biggest index / smallest cluster = 4096 / 512 = 8 */ ++ struct buffer_head *bh[PAGE_SIZE >> SECTOR_SHIFT]; ++ u32 bytes; ++ u32 nbufs; ++ u32 off; ++}; ++ ++enum ALLOCATE_OPT { ++ ALLOCATE_DEF = 0, // Allocate all clusters ++ ALLOCATE_MFT = 1, // Allocate for MFT ++}; ++ ++enum bitmap_mutex_classes { ++ BITMAP_MUTEX_CLUSTERS = 0, ++ BITMAP_MUTEX_MFT = 1, ++}; ++ ++struct wnd_bitmap { ++ struct super_block *sb; ++ struct rw_semaphore rw_lock; ++ ++ struct runs_tree run; ++ size_t nbits; ++ ++ size_t total_zeroes; // total number of free bits ++ u16 *free_bits; // free bits in each window ++ size_t nwnd; ++ u32 bits_last; // bits in last window ++ ++ struct rb_root start_tree; // extents, sorted by 'start' ++ struct rb_root count_tree; // extents, sorted by 'count + start' ++ size_t count; // extents count ++ ++ /* ++ * -1 Tree is activated but not updated (too many fragments) ++ * 0 - Tree is not activated ++ * 1 - Tree is activated and updated ++ */ ++ int uptodated; ++ size_t extent_min; // Minimal extent used while building ++ size_t extent_max; // Upper estimate of biggest free block ++ ++ /* Zone [bit, end) */ ++ size_t zone_bit; ++ size_t zone_end; ++ ++ bool set_tail; // not necessary in driver ++ bool inited; ++}; ++ ++typedef int (*NTFS_CMP_FUNC)(const void *key1, size_t len1, const void *key2, ++ size_t len2, const void *param); ++ ++enum index_mutex_classed { ++ INDEX_MUTEX_I30 = 0, ++ INDEX_MUTEX_SII = 1, ++ INDEX_MUTEX_SDH = 2, ++ INDEX_MUTEX_SO = 3, ++ INDEX_MUTEX_SQ = 4, ++ INDEX_MUTEX_SR = 5, ++ INDEX_MUTEX_TOTAL ++}; ++ ++/* ntfs_index - allocation unit inside directory */ ++struct ntfs_index { ++ struct runs_tree bitmap_run; ++ struct runs_tree alloc_run; ++ /* read/write access to 'bitmap_run'/'alloc_run' while ntfs_readdir */ ++ struct rw_semaphore run_lock; ++ ++ /*TODO: remove 'cmp'*/ ++ NTFS_CMP_FUNC cmp; ++ ++ u8 index_bits; // log2(root->index_block_size) ++ u8 idx2vbn_bits; // log2(root->index_block_clst) ++ u8 vbn2vbo_bits; // index_block_size < cluster? 9 : cluster_bits ++ u8 type; // index_mutex_classed ++}; ++ ++/* Minimum mft zone */ ++#define NTFS_MIN_MFT_ZONE 100 ++ ++/* ntfs file system in-core superblock data */ ++struct ntfs_sb_info { ++ struct super_block *sb; ++ ++ u32 discard_granularity; ++ u64 discard_granularity_mask_inv; // ~(discard_granularity_mask_inv-1) ++ ++ u32 cluster_size; // bytes per cluster ++ u32 cluster_mask; // == cluster_size - 1 ++ u64 cluster_mask_inv; // ~(cluster_size - 1) ++ u32 block_mask; // sb->s_blocksize - 1 ++ u32 blocks_per_cluster; // cluster_size / sb->s_blocksize ++ ++ u32 record_size; ++ u32 sector_size; ++ u32 index_size; ++ ++ u8 sector_bits; ++ u8 cluster_bits; ++ u8 record_bits; ++ ++ u64 maxbytes; // Maximum size for normal files ++ u64 maxbytes_sparse; // Maximum size for sparse file ++ ++ u32 flags; // See NTFS_FLAGS_XXX ++ ++ CLST bad_clusters; // The count of marked bad clusters ++ ++ u16 max_bytes_per_attr; // maximum attribute size in record ++ u16 attr_size_tr; // attribute size threshold (320 bytes) ++ ++ /* Records in $Extend */ ++ CLST objid_no; ++ CLST quota_no; ++ CLST reparse_no; ++ CLST usn_jrnl_no; ++ ++ struct ATTR_DEF_ENTRY *def_table; // attribute definition table ++ u32 def_entries; ++ u32 ea_max_size; ++ ++ struct MFT_REC *new_rec; ++ ++ u16 *upcase; ++ ++ struct { ++ u64 lbo, lbo2; ++ struct ntfs_inode *ni; ++ struct wnd_bitmap bitmap; // $MFT::Bitmap ++ /* ++ * MFT records [11-24) used to expand MFT itself ++ * They always marked as used in $MFT::Bitmap ++ * 'reserved_bitmap' contains real bitmap of these records ++ */ ++ ulong reserved_bitmap; // bitmap of used records [11 - 24) ++ size_t next_free; // The next record to allocate from ++ size_t used; // mft valid size in records ++ u32 recs_mirr; // Number of records in MFTMirr ++ u8 next_reserved; ++ u8 reserved_bitmap_inited; ++ } mft; ++ ++ struct { ++ struct wnd_bitmap bitmap; // $Bitmap::Data ++ CLST next_free_lcn; ++ } used; ++ ++ struct { ++ u64 size; // in bytes ++ u64 blocks; // in blocks ++ u64 ser_num; ++ struct ntfs_inode *ni; ++ __le16 flags; // cached current VOLUME_INFO::flags, VOLUME_FLAG_DIRTY ++ u8 major_ver; ++ u8 minor_ver; ++ char label[65]; ++ bool real_dirty; /* real fs state*/ ++ } volume; ++ ++ struct { ++ struct ntfs_index index_sii; ++ struct ntfs_index index_sdh; ++ struct ntfs_inode *ni; ++ u32 next_id; ++ u64 next_off; ++ ++ __le32 def_security_id; ++ } security; ++ ++ struct { ++ struct ntfs_index index_r; ++ struct ntfs_inode *ni; ++ u64 max_size; // 16K ++ } reparse; ++ ++ struct { ++ struct ntfs_index index_o; ++ struct ntfs_inode *ni; ++ } objid; ++ ++ struct { ++ struct mutex mtx_lznt; ++ struct lznt *lznt; ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ struct mutex mtx_xpress; ++ struct xpress_decompressor *xpress; ++ struct mutex mtx_lzx; ++ struct lzx_decompressor *lzx; ++#endif ++ } compress; ++ ++ struct ntfs_mount_options options; ++ struct ratelimit_state msg_ratelimit; ++}; ++ ++/* ++ * one MFT record(usually 1024 bytes), consists of attributes ++ */ ++struct mft_inode { ++ struct rb_node node; ++ struct ntfs_sb_info *sbi; ++ ++ struct MFT_REC *mrec; ++ struct ntfs_buffers nb; ++ ++ CLST rno; ++ bool dirty; ++}; ++ ++/* nested class for ntfs_inode::ni_lock */ ++enum ntfs_inode_mutex_lock_class { ++ NTFS_INODE_MUTEX_DIRTY, ++ NTFS_INODE_MUTEX_SECURITY, ++ NTFS_INODE_MUTEX_OBJID, ++ NTFS_INODE_MUTEX_REPARSE, ++ NTFS_INODE_MUTEX_NORMAL, ++ NTFS_INODE_MUTEX_PARENT, ++}; ++ ++/* ++ * ntfs inode - extends linux inode. consists of one or more mft inodes ++ */ ++struct ntfs_inode { ++ struct mft_inode mi; // base record ++ ++ /* ++ * Valid size: [0 - i_valid) - these range in file contains valid data ++ * Range [i_valid - inode->i_size) - contains 0 ++ * Usually i_valid <= inode->i_size ++ */ ++ u64 i_valid; ++ struct timespec64 i_crtime; ++ ++ struct mutex ni_lock; ++ ++ /* file attributes from std */ ++ enum FILE_ATTRIBUTE std_fa; ++ __le32 std_security_id; ++ ++ /* ++ * tree of mft_inode ++ * not empty when primary MFT record (usually 1024 bytes) can't save all attributes ++ * e.g. file becomes too fragmented or contains a lot of names ++ */ ++ struct rb_root mi_tree; ++ ++ /* ++ * This member is used in ntfs_readdir to ensure that all subrecords are loaded ++ */ ++ u8 mi_loaded; ++ ++ union { ++ struct ntfs_index dir; ++ struct { ++ struct rw_semaphore run_lock; ++ struct runs_tree run; ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ struct page *offs_page; ++#endif ++ } file; ++ }; ++ ++ struct { ++ struct runs_tree run; ++ struct ATTR_LIST_ENTRY *le; // 1K aligned memory ++ size_t size; ++ bool dirty; ++ } attr_list; ++ ++ size_t ni_flags; // NI_FLAG_XXX ++ ++ struct inode vfs_inode; ++}; ++ ++struct indx_node { ++ struct ntfs_buffers nb; ++ struct INDEX_BUFFER *index; ++}; ++ ++struct ntfs_fnd { ++ int level; ++ struct indx_node *nodes[20]; ++ struct NTFS_DE *de[20]; ++ struct NTFS_DE *root_de; ++}; ++ ++enum REPARSE_SIGN { ++ REPARSE_NONE = 0, ++ REPARSE_COMPRESSED = 1, ++ REPARSE_DEDUPLICATED = 2, ++ REPARSE_LINK = 3 ++}; ++ ++/* functions from attrib.c*/ ++int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni, ++ struct runs_tree *run, const CLST *vcn); ++int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, ++ CLST vcn, CLST lcn, CLST len, CLST *pre_alloc, ++ enum ALLOCATE_OPT opt, CLST *alen, const size_t fr, ++ CLST *new_lcn); ++int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY *le, struct mft_inode *mi, ++ u64 new_size, struct runs_tree *run, ++ struct ATTRIB **ins_attr, struct page *page); ++int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, struct runs_tree *run, ++ u64 new_size, const u64 *new_valid, bool keep_prealloc, ++ struct ATTRIB **ret); ++int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, ++ CLST *len, bool *new); ++int attr_data_read_resident(struct ntfs_inode *ni, struct page *page); ++int attr_data_write_resident(struct ntfs_inode *ni, struct page *page); ++int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, struct runs_tree *run, ++ CLST vcn); ++int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, struct runs_tree *run, ++ u64 from, u64 to); ++int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct runs_tree *run, u64 frame, u64 frames, ++ u8 frame_bits, u32 *ondisk_size, u64 *vbo_data); ++int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, ++ CLST frame, CLST *clst_data); ++int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, ++ u64 new_valid); ++int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); ++int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size); ++ ++/* functions from attrlist.c*/ ++void al_destroy(struct ntfs_inode *ni); ++bool al_verify(struct ntfs_inode *ni); ++int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr); ++struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le); ++struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le, ++ const struct ATTRIB *attr); ++struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le, ++ enum ATTR_TYPE type, const __le16 *name, ++ u8 name_len, const CLST *vcn); ++int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, ++ u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref, ++ struct ATTR_LIST_ENTRY **new_le); ++bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le); ++bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, ++ const __le16 *name, size_t name_len, ++ const struct MFT_REF *ref); ++int al_update(struct ntfs_inode *ni); ++static inline size_t al_aligned(size_t size) ++{ ++ return (size + 1023) & ~(size_t)1023; ++} ++ ++/* globals from bitfunc.c */ ++bool are_bits_clear(const ulong *map, size_t bit, size_t nbits); ++bool are_bits_set(const ulong *map, size_t bit, size_t nbits); ++size_t get_set_bits_ex(const ulong *map, size_t bit, size_t nbits); ++ ++/* globals from dir.c */ ++int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, ++ u8 *buf, int buf_len); ++int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, ++ struct cpu_str *uni, u32 max_ulen, ++ enum utf16_endian endian); ++struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, ++ struct ntfs_fnd *fnd); ++bool dir_is_empty(struct inode *dir); ++extern const struct file_operations ntfs_dir_operations; ++ ++/* globals from file.c*/ ++int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, ++ struct kstat *stat, u32 request_mask, u32 flags); ++void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn, ++ CLST len); ++int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ++ struct iattr *attr); ++int ntfs_file_open(struct inode *inode, struct file *file); ++int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ++ __u64 start, __u64 len); ++extern const struct inode_operations ntfs_special_inode_operations; ++extern const struct inode_operations ntfs_file_inode_operations; ++extern const struct file_operations ntfs_file_operations; ++ ++/* globals from frecord.c */ ++void ni_remove_mi(struct ntfs_inode *ni, struct mft_inode *mi); ++struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni); ++struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni); ++void ni_clear(struct ntfs_inode *ni); ++int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi); ++int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, ++ struct mft_inode **mi); ++struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY **entry_o, ++ enum ATTR_TYPE type, const __le16 *name, ++ u8 name_len, const CLST *vcn, ++ struct mft_inode **mi); ++struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY **le, ++ struct mft_inode **mi); ++struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, CLST vcn, ++ struct mft_inode **pmi); ++int ni_load_all_mi(struct ntfs_inode *ni); ++bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi); ++int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, size_t name_len, bool base_only, ++ const __le16 *id); ++int ni_create_attr_list(struct ntfs_inode *ni); ++int ni_expand_list(struct ntfs_inode *ni); ++int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, ++ const struct runs_tree *run, CLST svcn, CLST len, ++ __le16 flags, struct ATTRIB **new_attr, ++ struct mft_inode **mi); ++int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, ++ enum ATTR_TYPE type, const __le16 *name, u8 name_len, ++ struct ATTRIB **new_attr, struct mft_inode **mi); ++int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY *le); ++int ni_delete_all(struct ntfs_inode *ni); ++struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, ++ const struct cpu_str *uni, ++ const struct MFT_REF *home, ++ struct ATTR_LIST_ENTRY **entry); ++struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, ++ struct ATTR_LIST_ENTRY **entry); ++int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa); ++enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, ++ void *buffer); ++int ni_write_inode(struct inode *inode, int sync, const char *hint); ++#define _ni_write_inode(i, w) ni_write_inode(i, w, __func__) ++int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, ++ __u64 vbo, __u64 len); ++int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page); ++int ni_decompress_file(struct ntfs_inode *ni); ++int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, ++ u32 pages_per_frame); ++int ni_write_frame(struct ntfs_inode *ni, struct page **pages, ++ u32 pages_per_frame); ++ ++/* globals from fslog.c */ ++int log_replay(struct ntfs_inode *ni, bool *initialized); ++ ++/* globals from fsntfs.c */ ++bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes); ++int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, ++ bool simple); ++int ntfs_extend_init(struct ntfs_sb_info *sbi); ++int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi); ++const struct ATTR_DEF_ENTRY *ntfs_query_def(struct ntfs_sb_info *sbi, ++ enum ATTR_TYPE Type); ++int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, ++ CLST *new_lcn, CLST *new_len, ++ enum ALLOCATE_OPT opt); ++int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, ++ struct ntfs_inode *ni, struct mft_inode **mi); ++void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno); ++int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to); ++int ntfs_refresh_zone(struct ntfs_sb_info *sbi); ++int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait); ++enum NTFS_DIRTY_FLAGS { ++ NTFS_DIRTY_CLEAR = 0, ++ NTFS_DIRTY_DIRTY = 1, ++ NTFS_DIRTY_ERROR = 2, ++}; ++int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty); ++int ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer); ++int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes, ++ const void *buffer, int wait); ++int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ u64 vbo, const void *buf, size_t bytes); ++struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi, ++ const struct runs_tree *run, u64 vbo); ++int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb); ++int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, ++ struct NTFS_RECORD_HEADER *rhdr, u32 bytes, ++ struct ntfs_buffers *nb); ++int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, ++ u32 bytes, struct ntfs_buffers *nb); ++int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr, ++ struct ntfs_buffers *nb, int sync); ++int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ struct page **pages, u32 nr_pages, u64 vbo, u32 bytes, ++ u32 op); ++int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run); ++int ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ u64 vbo, u64 *lbo, u64 *bytes); ++struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST nRec, ++ bool dir); ++extern const u8 s_default_security[0x50]; ++bool is_sd_valid(const struct SECURITY_DESCRIPTOR_RELATIVE *sd, u32 len); ++int ntfs_security_init(struct ntfs_sb_info *sbi); ++int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, ++ struct SECURITY_DESCRIPTOR_RELATIVE **sd, ++ size_t *size); ++int ntfs_insert_security(struct ntfs_sb_info *sbi, ++ const struct SECURITY_DESCRIPTOR_RELATIVE *sd, ++ u32 size, __le32 *security_id, bool *inserted); ++int ntfs_reparse_init(struct ntfs_sb_info *sbi); ++int ntfs_objid_init(struct ntfs_sb_info *sbi); ++int ntfs_objid_remove(struct ntfs_sb_info *sbi, struct GUID *guid); ++int ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag, ++ const struct MFT_REF *ref); ++int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag, ++ const struct MFT_REF *ref); ++void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim); ++int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim); ++ ++/* globals from index.c */ ++int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit); ++void fnd_clear(struct ntfs_fnd *fnd); ++static inline struct ntfs_fnd *fnd_get(void) ++{ ++ return ntfs_zalloc(sizeof(struct ntfs_fnd)); ++} ++static inline void fnd_put(struct ntfs_fnd *fnd) ++{ ++ if (fnd) { ++ fnd_clear(fnd); ++ ntfs_free(fnd); ++ } ++} ++void indx_clear(struct ntfs_index *idx); ++int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, ++ const struct ATTRIB *attr, enum index_mutex_classed type); ++struct INDEX_ROOT *indx_get_root(struct ntfs_index *indx, struct ntfs_inode *ni, ++ struct ATTRIB **attr, struct mft_inode **mi); ++int indx_read(struct ntfs_index *idx, struct ntfs_inode *ni, CLST vbn, ++ struct indx_node **node); ++int indx_find(struct ntfs_index *indx, struct ntfs_inode *dir, ++ const struct INDEX_ROOT *root, const void *Key, size_t KeyLen, ++ const void *param, int *diff, struct NTFS_DE **entry, ++ struct ntfs_fnd *fnd); ++int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct INDEX_ROOT *root, struct NTFS_DE **entry, ++ struct ntfs_fnd *fnd); ++int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct INDEX_ROOT *root, struct NTFS_DE **entry, ++ size_t *off, struct ntfs_fnd *fnd); ++int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct NTFS_DE *new_de, const void *param, ++ struct ntfs_fnd *fnd); ++int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const void *key, u32 key_len, const void *param); ++int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, ++ const struct ATTR_FILE_NAME *fname, ++ const struct NTFS_DUP_INFO *dup, int sync); ++ ++/* globals from inode.c */ ++struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, ++ const struct cpu_str *name); ++int ntfs_set_size(struct inode *inode, u64 new_size); ++int reset_log_file(struct inode *inode); ++int ntfs_get_block(struct inode *inode, sector_t vbn, ++ struct buffer_head *bh_result, int create); ++int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc); ++int ntfs_sync_inode(struct inode *inode); ++int ntfs_flush_inodes(struct super_block *sb, struct inode *i1, ++ struct inode *i2); ++int inode_write_data(struct inode *inode, const void *data, size_t bytes); ++struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, ++ struct inode *dir, struct dentry *dentry, ++ const struct cpu_str *uni, umode_t mode, ++ dev_t dev, const char *symname, u32 size, ++ struct ntfs_fnd *fnd); ++int ntfs_link_inode(struct inode *inode, struct dentry *dentry); ++int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry); ++void ntfs_evict_inode(struct inode *inode); ++extern const struct inode_operations ntfs_link_inode_operations; ++extern const struct address_space_operations ntfs_aops; ++extern const struct address_space_operations ntfs_aops_cmpr; ++ ++/* globals from name_i.c*/ ++int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, ++ const struct cpu_str *uni); ++struct dentry *ntfs3_get_parent(struct dentry *child); ++ ++extern const struct inode_operations ntfs_dir_inode_operations; ++extern const struct inode_operations ntfs_special_inode_operations; ++ ++/* globals from record.c */ ++int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi); ++void mi_put(struct mft_inode *mi); ++int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno); ++int mi_read(struct mft_inode *mi, bool is_mft); ++struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr); ++// TODO: id? ++struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr, ++ enum ATTR_TYPE type, const __le16 *name, ++ size_t name_len, const __le16 *id); ++static inline struct ATTRIB *rec_find_attr_le(struct mft_inode *rec, ++ struct ATTR_LIST_ENTRY *le) ++{ ++ return mi_find_attr(rec, NULL, le->type, le_name(le), le->name_len, ++ &le->id); ++} ++int mi_write(struct mft_inode *mi, int wait); ++int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno, ++ __le16 flags, bool is_mft); ++void mi_mark_free(struct mft_inode *mi); ++struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, u32 asize, ++ u16 name_off); ++ ++bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr); ++bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes); ++int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr, ++ struct runs_tree *run, CLST len); ++static inline bool mi_is_ref(const struct mft_inode *mi, ++ const struct MFT_REF *ref) ++{ ++ if (le32_to_cpu(ref->low) != mi->rno) ++ return false; ++ if (ref->seq != mi->mrec->seq) ++ return false; ++ ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++ return le16_to_cpu(ref->high) == (mi->rno >> 32); ++#else ++ return !ref->high; ++#endif ++} ++ ++static inline void mi_get_ref(const struct mft_inode *mi, struct MFT_REF *ref) ++{ ++ ref->low = cpu_to_le32(mi->rno); ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++ ref->high = cpu_to_le16(mi->rno >> 32); ++#else ++ ref->high = 0; ++#endif ++ ref->seq = mi->mrec->seq; ++} ++ ++/* globals from run.c */ ++bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn, ++ CLST *len, size_t *index); ++void run_truncate(struct runs_tree *run, CLST vcn); ++void run_truncate_head(struct runs_tree *run, CLST vcn); ++void run_truncate_around(struct runs_tree *run, CLST vcn); ++bool run_lookup(const struct runs_tree *run, CLST vcn, size_t *Index); ++bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, ++ bool is_mft); ++bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len); ++bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn, ++ CLST *lcn, CLST *len); ++bool run_is_mapped_full(const struct runs_tree *run, CLST svcn, CLST evcn); ++ ++int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, ++ u32 run_buf_size, CLST *packed_vcns); ++int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, ++ CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf, ++ u32 run_buf_size); ++ ++#ifdef NTFS3_CHECK_FREE_CLST ++int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, ++ CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf, ++ u32 run_buf_size); ++#else ++#define run_unpack_ex run_unpack ++#endif ++int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn); ++ ++/* globals from super.c */ ++void *ntfs_set_shared(void *ptr, u32 bytes); ++void *ntfs_put_shared(void *ptr); ++void ntfs_unmap_meta(struct super_block *sb, CLST lcn, CLST len); ++int ntfs_discard(struct ntfs_sb_info *sbi, CLST Lcn, CLST Len); ++ ++/* globals from bitmap.c*/ ++int __init ntfs3_init_bitmap(void); ++void ntfs3_exit_bitmap(void); ++void wnd_close(struct wnd_bitmap *wnd); ++static inline size_t wnd_zeroes(const struct wnd_bitmap *wnd) ++{ ++ return wnd->total_zeroes; ++} ++int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits); ++int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits); ++int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits); ++bool wnd_is_free(struct wnd_bitmap *wnd, size_t bit, size_t bits); ++bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits); ++ ++/* Possible values for 'flags' 'wnd_find' */ ++#define BITMAP_FIND_MARK_AS_USED 0x01 ++#define BITMAP_FIND_FULL 0x02 ++size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, ++ size_t flags, size_t *allocated); ++int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits); ++void wnd_zone_set(struct wnd_bitmap *wnd, size_t Lcn, size_t Len); ++int ntfs_trim_fs(struct ntfs_sb_info *sbi, struct fstrim_range *range); ++ ++/* globals from upcase.c */ ++int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2, ++ const u16 *upcase, bool bothcase); ++int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2, ++ const u16 *upcase, bool bothcase); ++ ++/* globals from xattr.c */ ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++struct posix_acl *ntfs_get_acl(struct inode *inode, int type); ++int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, ++ struct posix_acl *acl, int type); ++int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, ++ struct inode *dir); ++#else ++#define ntfs_get_acl NULL ++#define ntfs_set_acl NULL ++#endif ++ ++int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode); ++int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, ++ int mask); ++ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size); ++extern const struct xattr_handler *ntfs_xattr_handlers[]; ++ ++int ntfs_save_wsl_perm(struct inode *inode); ++void ntfs_get_wsl_perm(struct inode *inode); ++ ++/* globals from lznt.c */ ++struct lznt *get_lznt_ctx(int level); ++size_t compress_lznt(const void *uncompressed, size_t uncompressed_size, ++ void *compressed, size_t compressed_size, ++ struct lznt *ctx); ++ssize_t decompress_lznt(const void *compressed, size_t compressed_size, ++ void *uncompressed, size_t uncompressed_size); ++ ++static inline bool is_ntfs3(struct ntfs_sb_info *sbi) ++{ ++ return sbi->volume.major_ver >= 3; ++} ++ ++/*(sb->s_flags & SB_ACTIVE)*/ ++static inline bool is_mounted(struct ntfs_sb_info *sbi) ++{ ++ return !!sbi->sb->s_root; ++} ++ ++static inline bool ntfs_is_meta_file(struct ntfs_sb_info *sbi, CLST rno) ++{ ++ return rno < MFT_REC_FREE || rno == sbi->objid_no || ++ rno == sbi->quota_no || rno == sbi->reparse_no || ++ rno == sbi->usn_jrnl_no; ++} ++ ++static inline void ntfs_unmap_page(struct page *page) ++{ ++ kunmap(page); ++ put_page(page); ++} ++ ++static inline struct page *ntfs_map_page(struct address_space *mapping, ++ unsigned long index) ++{ ++ struct page *page = read_mapping_page(mapping, index, NULL); ++ ++ if (!IS_ERR(page)) { ++ kmap(page); ++ if (!PageError(page)) ++ return page; ++ ntfs_unmap_page(page); ++ return ERR_PTR(-EIO); ++ } ++ return page; ++} ++ ++static inline size_t wnd_zone_bit(const struct wnd_bitmap *wnd) ++{ ++ return wnd->zone_bit; ++} ++ ++static inline size_t wnd_zone_len(const struct wnd_bitmap *wnd) ++{ ++ return wnd->zone_end - wnd->zone_bit; ++} ++ ++static inline void run_init(struct runs_tree *run) ++{ ++ run->runs = NULL; ++ run->count = 0; ++ run->allocated = 0; ++} ++ ++static inline struct runs_tree *run_alloc(void) ++{ ++ return ntfs_zalloc(sizeof(struct runs_tree)); ++} ++ ++static inline void run_close(struct runs_tree *run) ++{ ++ ntfs_vfree(run->runs); ++ memset(run, 0, sizeof(*run)); ++} ++ ++static inline void run_free(struct runs_tree *run) ++{ ++ if (run) { ++ ntfs_vfree(run->runs); ++ ntfs_free(run); ++ } ++} ++ ++static inline bool run_is_empty(struct runs_tree *run) ++{ ++ return !run->count; ++} ++ ++/* NTFS uses quad aligned bitmaps */ ++static inline size_t bitmap_size(size_t bits) ++{ ++ return QuadAlign((bits + 7) >> 3); ++} ++ ++#define _100ns2seconds 10000000 ++#define SecondsToStartOf1970 0x00000002B6109100 ++ ++#define NTFS_TIME_GRAN 100 ++ ++/* ++ * kernel2nt ++ * ++ * converts in-memory kernel timestamp into nt time ++ */ ++static inline __le64 kernel2nt(const struct timespec64 *ts) ++{ ++ // 10^7 units of 100 nanoseconds one second ++ return cpu_to_le64(_100ns2seconds * ++ (ts->tv_sec + SecondsToStartOf1970) + ++ ts->tv_nsec / NTFS_TIME_GRAN); ++} ++ ++/* ++ * nt2kernel ++ * ++ * converts on-disk nt time into kernel timestamp ++ */ ++static inline void nt2kernel(const __le64 tm, struct timespec64 *ts) ++{ ++ u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970; ++ ++ // WARNING: do_div changes its first argument(!) ++ ts->tv_nsec = do_div(t, _100ns2seconds) * 100; ++ ts->tv_sec = t; ++} ++ ++static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++/* Align up on cluster boundary */ ++static inline u64 ntfs_up_cluster(const struct ntfs_sb_info *sbi, u64 size) ++{ ++ return (size + sbi->cluster_mask) & sbi->cluster_mask_inv; ++} ++ ++/* Align up on cluster boundary */ ++static inline u64 ntfs_up_block(const struct super_block *sb, u64 size) ++{ ++ return (size + sb->s_blocksize - 1) & ~(u64)(sb->s_blocksize - 1); ++} ++ ++static inline CLST bytes_to_cluster(const struct ntfs_sb_info *sbi, u64 size) ++{ ++ return (size + sbi->cluster_mask) >> sbi->cluster_bits; ++} ++ ++static inline u64 bytes_to_block(const struct super_block *sb, u64 size) ++{ ++ return (size + sb->s_blocksize - 1) >> sb->s_blocksize_bits; ++} ++ ++static inline struct buffer_head *ntfs_bread(struct super_block *sb, ++ sector_t block) ++{ ++ struct buffer_head *bh = sb_bread(sb, block); ++ ++ if (bh) ++ return bh; ++ ++ ntfs_err(sb, "failed to read volume at offset 0x%llx", ++ (u64)block << sb->s_blocksize_bits); ++ return NULL; ++} ++ ++static inline bool is_power_of2(size_t v) ++{ ++ return v && !(v & (v - 1)); ++} ++ ++static inline struct ntfs_inode *ntfs_i(struct inode *inode) ++{ ++ return container_of(inode, struct ntfs_inode, vfs_inode); ++} ++ ++static inline bool is_compressed(const struct ntfs_inode *ni) ++{ ++ return (ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) || ++ (ni->ni_flags & NI_FLAG_COMPRESSED_MASK); ++} ++ ++static inline int ni_ext_compress_bits(const struct ntfs_inode *ni) ++{ ++ return 0xb + (ni->ni_flags & NI_FLAG_COMPRESSED_MASK); ++} ++ ++/* bits - 0xc, 0xd, 0xe, 0xf, 0x10 */ ++static inline void ni_set_ext_compress_bits(struct ntfs_inode *ni, u8 bits) ++{ ++ ni->ni_flags |= (bits - 0xb) & NI_FLAG_COMPRESSED_MASK; ++} ++ ++static inline bool is_dedup(const struct ntfs_inode *ni) ++{ ++ return ni->ni_flags & NI_FLAG_DEDUPLICATED; ++} ++ ++static inline bool is_encrypted(const struct ntfs_inode *ni) ++{ ++ return ni->std_fa & FILE_ATTRIBUTE_ENCRYPTED; ++} ++ ++static inline bool is_sparsed(const struct ntfs_inode *ni) ++{ ++ return ni->std_fa & FILE_ATTRIBUTE_SPARSE_FILE; ++} ++ ++static inline int is_resident(struct ntfs_inode *ni) ++{ ++ return ni->ni_flags & NI_FLAG_RESIDENT; ++} ++ ++static inline void le16_sub_cpu(__le16 *var, u16 val) ++{ ++ *var = cpu_to_le16(le16_to_cpu(*var) - val); ++} ++ ++static inline void le32_sub_cpu(__le32 *var, u32 val) ++{ ++ *var = cpu_to_le32(le32_to_cpu(*var) - val); ++} ++ ++static inline void nb_put(struct ntfs_buffers *nb) ++{ ++ u32 i, nbufs = nb->nbufs; ++ ++ if (!nbufs) ++ return; ++ ++ for (i = 0; i < nbufs; i++) ++ put_bh(nb->bh[i]); ++ nb->nbufs = 0; ++} ++ ++static inline void put_indx_node(struct indx_node *in) ++{ ++ if (!in) ++ return; ++ ++ ntfs_free(in->index); ++ nb_put(&in->nb); ++ ntfs_free(in); ++} ++ ++static inline void mi_clear(struct mft_inode *mi) ++{ ++ nb_put(&mi->nb); ++ ntfs_free(mi->mrec); ++ mi->mrec = NULL; ++} ++ ++static inline void ni_lock(struct ntfs_inode *ni) ++{ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_NORMAL); ++} ++ ++static inline void ni_lock_dir(struct ntfs_inode *ni) ++{ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_PARENT); ++} ++ ++static inline void ni_unlock(struct ntfs_inode *ni) ++{ ++ mutex_unlock(&ni->ni_lock); ++} ++ ++static inline int ni_trylock(struct ntfs_inode *ni) ++{ ++ return mutex_trylock(&ni->ni_lock); ++} ++ ++static inline int attr_load_runs_attr(struct ntfs_inode *ni, ++ struct ATTRIB *attr, ++ struct runs_tree *run, CLST vcn) ++{ ++ return attr_load_runs_vcn(ni, attr->type, attr_name(attr), ++ attr->name_len, run, vcn); ++} ++ ++static inline void le64_sub_cpu(__le64 *var, u64 val) ++{ ++ *var = cpu_to_le64(le64_to_cpu(*var) - val); ++} +diff --git a/src/upcase.c b/src/upcase.c +new file mode 100644 +index 0000000000000000000000000000000000000000..9617382aca64b3ee322f7f769fbff38b8db52dbb +--- /dev/null ++++ b/src/upcase.c +@@ -0,0 +1,105 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr) ++{ ++ if (chr < 'a') ++ return chr; ++ ++ if (chr <= 'z') ++ return chr - ('a' - 'A'); ++ ++ return upcase[chr]; ++} ++ ++/* ++ * Thanks Kari Argillander for idea and implementation 'bothcase' ++ * ++ * Straigth way to compare names: ++ * - case insensitive ++ * - if name equals and 'bothcases' then ++ * - case sensitive ++ * 'Straigth way' code scans input names twice in worst case ++ * Optimized code scans input names only once ++ */ ++int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2, ++ const u16 *upcase, bool bothcase) ++{ ++ int diff1 = 0; ++ int diff2; ++ size_t len = min(l1, l2); ++ ++ if (!bothcase && upcase) ++ goto case_insentive; ++ ++ for (; len; s1++, s2++, len--) { ++ diff1 = le16_to_cpu(*s1) - le16_to_cpu(*s2); ++ if (diff1) { ++ if (bothcase && upcase) ++ goto case_insentive; ++ ++ return diff1; ++ } ++ } ++ return l1 - l2; ++ ++case_insentive: ++ for (; len; s1++, s2++, len--) { ++ diff2 = upcase_unicode_char(upcase, le16_to_cpu(*s1)) - ++ upcase_unicode_char(upcase, le16_to_cpu(*s2)); ++ if (diff2) ++ return diff2; ++ } ++ ++ diff2 = l1 - l2; ++ return diff2 ? diff2 : diff1; ++} ++ ++int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2, ++ const u16 *upcase, bool bothcase) ++{ ++ const u16 *s1 = uni1->name; ++ const __le16 *s2 = uni2->name; ++ size_t l1 = uni1->len; ++ size_t l2 = uni2->len; ++ size_t len = min(l1, l2); ++ int diff1 = 0; ++ int diff2; ++ ++ if (!bothcase && upcase) ++ goto case_insentive; ++ ++ for (; len; s1++, s2++, len--) { ++ diff1 = *s1 - le16_to_cpu(*s2); ++ if (diff1) { ++ if (bothcase && upcase) ++ goto case_insentive; ++ ++ return diff1; ++ } ++ } ++ return l1 - l2; ++ ++case_insentive: ++ for (; len; s1++, s2++, len--) { ++ diff2 = upcase_unicode_char(upcase, *s1) - ++ upcase_unicode_char(upcase, le16_to_cpu(*s2)); ++ if (diff2) ++ return diff2; ++ } ++ ++ diff2 = l1 - l2; ++ return diff2 ? diff2 : diff1; ++} +-- +2.31.1 + diff --git a/SOURCES/0002-fs-ntfs3-Add-initialization-of-super-block.patch b/SOURCES/0002-fs-ntfs3-Add-initialization-of-super-block.patch new file mode 100644 index 0000000..815c5dc --- /dev/null +++ b/SOURCES/0002-fs-ntfs3-Add-initialization-of-super-block.patch @@ -0,0 +1,8777 @@ +From 82cae269cfa953032fbb8980a7d554d60fb00b17 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:29 +0300 +Subject: [Backport 82cae269cfa9] src: Add initialization of super block + +This adds initialization of super block + +Signed-off-by: Konstantin Komarov +--- + src/fsntfs.c | 2551 +++++++++++++++++++++++++++++++++++++++++++ + src/index.c | 2647 +++++++++++++++++++++++++++++++++++++++++++++ + src/inode.c | 2029 ++++++++++++++++++++++++++++++++++ + src/super.c | 1504 ++++++++++++++++++++++++++ + 4 files changed, 8731 insertions(+) + create mode 100644 src/fsntfs.c + create mode 100644 src/index.c + create mode 100644 src/inode.c + create mode 100644 src/super.c + +diff --git a/src/fsntfs.c b/src/fsntfs.c +new file mode 100644 +index 0000000000000000000000000000000000000000..92140050fb6c250ce263832d86ff15257cc035e4 +--- /dev/null ++++ b/src/fsntfs.c +@@ -0,0 +1,2551 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++// clang-format off ++const struct cpu_str NAME_MFT = { ++ 4, 0, { '$', 'M', 'F', 'T' }, ++}; ++const struct cpu_str NAME_MIRROR = { ++ 8, 0, { '$', 'M', 'F', 'T', 'M', 'i', 'r', 'r' }, ++}; ++const struct cpu_str NAME_LOGFILE = { ++ 8, 0, { '$', 'L', 'o', 'g', 'F', 'i', 'l', 'e' }, ++}; ++const struct cpu_str NAME_VOLUME = { ++ 7, 0, { '$', 'V', 'o', 'l', 'u', 'm', 'e' }, ++}; ++const struct cpu_str NAME_ATTRDEF = { ++ 8, 0, { '$', 'A', 't', 't', 'r', 'D', 'e', 'f' }, ++}; ++const struct cpu_str NAME_ROOT = { ++ 1, 0, { '.' }, ++}; ++const struct cpu_str NAME_BITMAP = { ++ 7, 0, { '$', 'B', 'i', 't', 'm', 'a', 'p' }, ++}; ++const struct cpu_str NAME_BOOT = { ++ 5, 0, { '$', 'B', 'o', 'o', 't' }, ++}; ++const struct cpu_str NAME_BADCLUS = { ++ 8, 0, { '$', 'B', 'a', 'd', 'C', 'l', 'u', 's' }, ++}; ++const struct cpu_str NAME_QUOTA = { ++ 6, 0, { '$', 'Q', 'u', 'o', 't', 'a' }, ++}; ++const struct cpu_str NAME_SECURE = { ++ 7, 0, { '$', 'S', 'e', 'c', 'u', 'r', 'e' }, ++}; ++const struct cpu_str NAME_UPCASE = { ++ 7, 0, { '$', 'U', 'p', 'C', 'a', 's', 'e' }, ++}; ++const struct cpu_str NAME_EXTEND = { ++ 7, 0, { '$', 'E', 'x', 't', 'e', 'n', 'd' }, ++}; ++const struct cpu_str NAME_OBJID = { ++ 6, 0, { '$', 'O', 'b', 'j', 'I', 'd' }, ++}; ++const struct cpu_str NAME_REPARSE = { ++ 8, 0, { '$', 'R', 'e', 'p', 'a', 'r', 's', 'e' }, ++}; ++const struct cpu_str NAME_USNJRNL = { ++ 8, 0, { '$', 'U', 's', 'n', 'J', 'r', 'n', 'l' }, ++}; ++const __le16 BAD_NAME[4] = { ++ cpu_to_le16('$'), cpu_to_le16('B'), cpu_to_le16('a'), cpu_to_le16('d'), ++}; ++const __le16 I30_NAME[4] = { ++ cpu_to_le16('$'), cpu_to_le16('I'), cpu_to_le16('3'), cpu_to_le16('0'), ++}; ++const __le16 SII_NAME[4] = { ++ cpu_to_le16('$'), cpu_to_le16('S'), cpu_to_le16('I'), cpu_to_le16('I'), ++}; ++const __le16 SDH_NAME[4] = { ++ cpu_to_le16('$'), cpu_to_le16('S'), cpu_to_le16('D'), cpu_to_le16('H'), ++}; ++const __le16 SDS_NAME[4] = { ++ cpu_to_le16('$'), cpu_to_le16('S'), cpu_to_le16('D'), cpu_to_le16('S'), ++}; ++const __le16 SO_NAME[2] = { ++ cpu_to_le16('$'), cpu_to_le16('O'), ++}; ++const __le16 SQ_NAME[2] = { ++ cpu_to_le16('$'), cpu_to_le16('Q'), ++}; ++const __le16 SR_NAME[2] = { ++ cpu_to_le16('$'), cpu_to_le16('R'), ++}; ++ ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++const __le16 WOF_NAME[17] = { ++ cpu_to_le16('W'), cpu_to_le16('o'), cpu_to_le16('f'), cpu_to_le16('C'), ++ cpu_to_le16('o'), cpu_to_le16('m'), cpu_to_le16('p'), cpu_to_le16('r'), ++ cpu_to_le16('e'), cpu_to_le16('s'), cpu_to_le16('s'), cpu_to_le16('e'), ++ cpu_to_le16('d'), cpu_to_le16('D'), cpu_to_le16('a'), cpu_to_le16('t'), ++ cpu_to_le16('a'), ++}; ++#endif ++ ++// clang-format on ++ ++/* ++ * ntfs_fix_pre_write ++ * ++ * inserts fixups into 'rhdr' before writing to disk ++ */ ++bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes) ++{ ++ u16 *fixup, *ptr; ++ u16 sample; ++ u16 fo = le16_to_cpu(rhdr->fix_off); ++ u16 fn = le16_to_cpu(rhdr->fix_num); ++ ++ if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || ++ fn * SECTOR_SIZE > bytes) { ++ return false; ++ } ++ ++ /* Get fixup pointer */ ++ fixup = Add2Ptr(rhdr, fo); ++ ++ if (*fixup >= 0x7FFF) ++ *fixup = 1; ++ else ++ *fixup += 1; ++ ++ sample = *fixup; ++ ++ ptr = Add2Ptr(rhdr, SECTOR_SIZE - sizeof(short)); ++ ++ while (fn--) { ++ *++fixup = *ptr; ++ *ptr = sample; ++ ptr += SECTOR_SIZE / sizeof(short); ++ } ++ return true; ++} ++ ++/* ++ * ntfs_fix_post_read ++ * ++ * remove fixups after reading from disk ++ * Returns < 0 if error, 0 if ok, 1 if need to update fixups ++ */ ++int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, ++ bool simple) ++{ ++ int ret; ++ u16 *fixup, *ptr; ++ u16 sample, fo, fn; ++ ++ fo = le16_to_cpu(rhdr->fix_off); ++ fn = simple ? ((bytes >> SECTOR_SHIFT) + 1) ++ : le16_to_cpu(rhdr->fix_num); ++ ++ /* Check errors */ ++ if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || ++ fn * SECTOR_SIZE > bytes) { ++ return -EINVAL; /* native chkntfs returns ok! */ ++ } ++ ++ /* Get fixup pointer */ ++ fixup = Add2Ptr(rhdr, fo); ++ sample = *fixup; ++ ptr = Add2Ptr(rhdr, SECTOR_SIZE - sizeof(short)); ++ ret = 0; ++ ++ while (fn--) { ++ /* Test current word */ ++ if (*ptr != sample) { ++ /* Fixup does not match! Is it serious error? */ ++ ret = -E_NTFS_FIXUP; ++ } ++ ++ /* Replace fixup */ ++ *ptr = *++fixup; ++ ptr += SECTOR_SIZE / sizeof(short); ++ } ++ ++ return ret; ++} ++ ++/* ++ * ntfs_extend_init ++ * ++ * loads $Extend file ++ */ ++int ntfs_extend_init(struct ntfs_sb_info *sbi) ++{ ++ int err; ++ struct super_block *sb = sbi->sb; ++ struct inode *inode, *inode2; ++ struct MFT_REF ref; ++ ++ if (sbi->volume.major_ver < 3) { ++ ntfs_notice(sb, "Skip $Extend 'cause NTFS version"); ++ return 0; ++ } ++ ++ ref.low = cpu_to_le32(MFT_REC_EXTEND); ++ ref.high = 0; ++ ref.seq = cpu_to_le16(MFT_REC_EXTEND); ++ inode = ntfs_iget5(sb, &ref, &NAME_EXTEND); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $Extend."); ++ inode = NULL; ++ goto out; ++ } ++ ++ /* if ntfs_iget5 reads from disk it never returns bad inode */ ++ if (!S_ISDIR(inode->i_mode)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Try to find $ObjId */ ++ inode2 = dir_search_u(inode, &NAME_OBJID, NULL); ++ if (inode2 && !IS_ERR(inode2)) { ++ if (is_bad_inode(inode2)) { ++ iput(inode2); ++ } else { ++ sbi->objid.ni = ntfs_i(inode2); ++ sbi->objid_no = inode2->i_ino; ++ } ++ } ++ ++ /* Try to find $Quota */ ++ inode2 = dir_search_u(inode, &NAME_QUOTA, NULL); ++ if (inode2 && !IS_ERR(inode2)) { ++ sbi->quota_no = inode2->i_ino; ++ iput(inode2); ++ } ++ ++ /* Try to find $Reparse */ ++ inode2 = dir_search_u(inode, &NAME_REPARSE, NULL); ++ if (inode2 && !IS_ERR(inode2)) { ++ sbi->reparse.ni = ntfs_i(inode2); ++ sbi->reparse_no = inode2->i_ino; ++ } ++ ++ /* Try to find $UsnJrnl */ ++ inode2 = dir_search_u(inode, &NAME_USNJRNL, NULL); ++ if (inode2 && !IS_ERR(inode2)) { ++ sbi->usn_jrnl_no = inode2->i_ino; ++ iput(inode2); ++ } ++ ++ err = 0; ++out: ++ iput(inode); ++ return err; ++} ++ ++int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) ++{ ++ int err = 0; ++ struct super_block *sb = sbi->sb; ++ bool initialized = false; ++ struct MFT_REF ref; ++ struct inode *inode; ++ ++ /* Check for 4GB */ ++ if (ni->vfs_inode.i_size >= 0x100000000ull) { ++ ntfs_err(sb, "\x24LogFile is too big"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ sbi->flags |= NTFS_FLAGS_LOG_REPLAYING; ++ ++ ref.low = cpu_to_le32(MFT_REC_MFT); ++ ref.high = 0; ++ ref.seq = cpu_to_le16(1); ++ ++ inode = ntfs_iget5(sb, &ref, NULL); ++ ++ if (IS_ERR(inode)) ++ inode = NULL; ++ ++ if (!inode) { ++ /* Try to use mft copy */ ++ u64 t64 = sbi->mft.lbo; ++ ++ sbi->mft.lbo = sbi->mft.lbo2; ++ inode = ntfs_iget5(sb, &ref, NULL); ++ sbi->mft.lbo = t64; ++ if (IS_ERR(inode)) ++ inode = NULL; ++ } ++ ++ if (!inode) { ++ err = -EINVAL; ++ ntfs_err(sb, "Failed to load $MFT."); ++ goto out; ++ } ++ ++ sbi->mft.ni = ntfs_i(inode); ++ ++ /* LogFile should not contains attribute list */ ++ err = ni_load_all_mi(sbi->mft.ni); ++ if (!err) ++ err = log_replay(ni, &initialized); ++ ++ iput(inode); ++ sbi->mft.ni = NULL; ++ ++ sync_blockdev(sb->s_bdev); ++ invalidate_bdev(sb->s_bdev); ++ ++ if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) { ++ err = 0; ++ goto out; ++ } ++ ++ if (sb_rdonly(sb) || !initialized) ++ goto out; ++ ++ /* fill LogFile by '-1' if it is initialized */ ++ err = ntfs_bio_fill_1(sbi, &ni->file.run); ++ ++out: ++ sbi->flags &= ~NTFS_FLAGS_LOG_REPLAYING; ++ ++ return err; ++} ++ ++/* ++ * ntfs_query_def ++ * ++ * returns current ATTR_DEF_ENTRY for given attribute type ++ */ ++const struct ATTR_DEF_ENTRY *ntfs_query_def(struct ntfs_sb_info *sbi, ++ enum ATTR_TYPE type) ++{ ++ int type_in = le32_to_cpu(type); ++ size_t min_idx = 0; ++ size_t max_idx = sbi->def_entries - 1; ++ ++ while (min_idx <= max_idx) { ++ size_t i = min_idx + ((max_idx - min_idx) >> 1); ++ const struct ATTR_DEF_ENTRY *entry = sbi->def_table + i; ++ int diff = le32_to_cpu(entry->type) - type_in; ++ ++ if (!diff) ++ return entry; ++ if (diff < 0) ++ min_idx = i + 1; ++ else if (i) ++ max_idx = i - 1; ++ else ++ return NULL; ++ } ++ return NULL; ++} ++ ++/* ++ * ntfs_look_for_free_space ++ * ++ * looks for a free space in bitmap ++ */ ++int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, ++ CLST *new_lcn, CLST *new_len, ++ enum ALLOCATE_OPT opt) ++{ ++ int err; ++ struct super_block *sb = sbi->sb; ++ size_t a_lcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen; ++ struct wnd_bitmap *wnd = &sbi->used.bitmap; ++ ++ down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); ++ if (opt & ALLOCATE_MFT) { ++ CLST alen; ++ ++ zlen = wnd_zone_len(wnd); ++ ++ if (!zlen) { ++ err = ntfs_refresh_zone(sbi); ++ if (err) ++ goto out; ++ ++ zlen = wnd_zone_len(wnd); ++ ++ if (!zlen) { ++ ntfs_err(sbi->sb, ++ "no free space to extend mft"); ++ err = -ENOSPC; ++ goto out; ++ } ++ } ++ ++ lcn = wnd_zone_bit(wnd); ++ alen = zlen > len ? len : zlen; ++ ++ wnd_zone_set(wnd, lcn + alen, zlen - alen); ++ ++ err = wnd_set_used(wnd, lcn, alen); ++ if (err) ++ goto out; ++ ++ *new_lcn = lcn; ++ *new_len = alen; ++ goto ok; ++ } ++ ++ /* ++ * 'Cause cluster 0 is always used this value means that we should use ++ * cached value of 'next_free_lcn' to improve performance ++ */ ++ if (!lcn) ++ lcn = sbi->used.next_free_lcn; ++ ++ if (lcn >= wnd->nbits) ++ lcn = 0; ++ ++ *new_len = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &a_lcn); ++ if (*new_len) { ++ *new_lcn = a_lcn; ++ goto ok; ++ } ++ ++ /* Try to use clusters from MftZone */ ++ zlen = wnd_zone_len(wnd); ++ zeroes = wnd_zeroes(wnd); ++ ++ /* Check too big request */ ++ if (len > zeroes + zlen) ++ goto no_space; ++ ++ if (zlen <= NTFS_MIN_MFT_ZONE) ++ goto no_space; ++ ++ /* How many clusters to cat from zone */ ++ zlcn = wnd_zone_bit(wnd); ++ zlen2 = zlen >> 1; ++ ztrim = len > zlen ? zlen : (len > zlen2 ? len : zlen2); ++ new_zlen = zlen - ztrim; ++ ++ if (new_zlen < NTFS_MIN_MFT_ZONE) { ++ new_zlen = NTFS_MIN_MFT_ZONE; ++ if (new_zlen > zlen) ++ new_zlen = zlen; ++ } ++ ++ wnd_zone_set(wnd, zlcn, new_zlen); ++ ++ /* allocate continues clusters */ ++ *new_len = ++ wnd_find(wnd, len, 0, ++ BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &a_lcn); ++ if (*new_len) { ++ *new_lcn = a_lcn; ++ goto ok; ++ } ++ ++no_space: ++ up_write(&wnd->rw_lock); ++ ++ return -ENOSPC; ++ ++ok: ++ err = 0; ++ ++ ntfs_unmap_meta(sb, *new_lcn, *new_len); ++ ++ if (opt & ALLOCATE_MFT) ++ goto out; ++ ++ /* Set hint for next requests */ ++ sbi->used.next_free_lcn = *new_lcn + *new_len; ++ ++out: ++ up_write(&wnd->rw_lock); ++ return err; ++} ++ ++/* ++ * ntfs_extend_mft ++ * ++ * allocates additional MFT records ++ * sbi->mft.bitmap is locked for write ++ * ++ * NOTE: recursive: ++ * ntfs_look_free_mft -> ++ * ntfs_extend_mft -> ++ * attr_set_size -> ++ * ni_insert_nonresident -> ++ * ni_insert_attr -> ++ * ni_ins_attr_ext -> ++ * ntfs_look_free_mft -> ++ * ntfs_extend_mft ++ * To avoid recursive always allocate space for two new mft records ++ * see attrib.c: "at least two mft to avoid recursive loop" ++ */ ++static int ntfs_extend_mft(struct ntfs_sb_info *sbi) ++{ ++ int err; ++ struct ntfs_inode *ni = sbi->mft.ni; ++ size_t new_mft_total; ++ u64 new_mft_bytes, new_bitmap_bytes; ++ struct ATTRIB *attr; ++ struct wnd_bitmap *wnd = &sbi->mft.bitmap; ++ ++ new_mft_total = (wnd->nbits + MFT_INCREASE_CHUNK + 127) & (CLST)~127; ++ new_mft_bytes = (u64)new_mft_total << sbi->record_bits; ++ ++ /* Step 1: Resize $MFT::DATA */ ++ down_write(&ni->file.run_lock); ++ err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, ++ new_mft_bytes, NULL, false, &attr); ++ ++ if (err) { ++ up_write(&ni->file.run_lock); ++ goto out; ++ } ++ ++ attr->nres.valid_size = attr->nres.data_size; ++ new_mft_total = le64_to_cpu(attr->nres.alloc_size) >> sbi->record_bits; ++ ni->mi.dirty = true; ++ ++ /* Step 2: Resize $MFT::BITMAP */ ++ new_bitmap_bytes = bitmap_size(new_mft_total); ++ ++ err = attr_set_size(ni, ATTR_BITMAP, NULL, 0, &sbi->mft.bitmap.run, ++ new_bitmap_bytes, &new_bitmap_bytes, true, NULL); ++ ++ /* Refresh Mft Zone if necessary */ ++ down_write_nested(&sbi->used.bitmap.rw_lock, BITMAP_MUTEX_CLUSTERS); ++ ++ ntfs_refresh_zone(sbi); ++ ++ up_write(&sbi->used.bitmap.rw_lock); ++ up_write(&ni->file.run_lock); ++ ++ if (err) ++ goto out; ++ ++ err = wnd_extend(wnd, new_mft_total); ++ ++ if (err) ++ goto out; ++ ++ ntfs_clear_mft_tail(sbi, sbi->mft.used, new_mft_total); ++ ++ err = _ni_write_inode(&ni->vfs_inode, 0); ++out: ++ return err; ++} ++ ++/* ++ * ntfs_look_free_mft ++ * ++ * looks for a free MFT record ++ */ ++int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, ++ struct ntfs_inode *ni, struct mft_inode **mi) ++{ ++ int err = 0; ++ size_t zbit, zlen, from, to, fr; ++ size_t mft_total; ++ struct MFT_REF ref; ++ struct super_block *sb = sbi->sb; ++ struct wnd_bitmap *wnd = &sbi->mft.bitmap; ++ u32 ir; ++ ++ static_assert(sizeof(sbi->mft.reserved_bitmap) * 8 >= ++ MFT_REC_FREE - MFT_REC_RESERVED); ++ ++ if (!mft) ++ down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT); ++ ++ zlen = wnd_zone_len(wnd); ++ ++ /* Always reserve space for MFT */ ++ if (zlen) { ++ if (mft) { ++ zbit = wnd_zone_bit(wnd); ++ *rno = zbit; ++ wnd_zone_set(wnd, zbit + 1, zlen - 1); ++ } ++ goto found; ++ } ++ ++ /* No MFT zone. find the nearest to '0' free MFT */ ++ if (!wnd_find(wnd, 1, MFT_REC_FREE, 0, &zbit)) { ++ /* Resize MFT */ ++ mft_total = wnd->nbits; ++ ++ err = ntfs_extend_mft(sbi); ++ if (!err) { ++ zbit = mft_total; ++ goto reserve_mft; ++ } ++ ++ if (!mft || MFT_REC_FREE == sbi->mft.next_reserved) ++ goto out; ++ ++ err = 0; ++ ++ /* ++ * Look for free record reserved area [11-16) == ++ * [MFT_REC_RESERVED, MFT_REC_FREE ) MFT bitmap always ++ * marks it as used ++ */ ++ if (!sbi->mft.reserved_bitmap) { ++ /* Once per session create internal bitmap for 5 bits */ ++ sbi->mft.reserved_bitmap = 0xFF; ++ ++ ref.high = 0; ++ for (ir = MFT_REC_RESERVED; ir < MFT_REC_FREE; ir++) { ++ struct inode *i; ++ struct ntfs_inode *ni; ++ struct MFT_REC *mrec; ++ ++ ref.low = cpu_to_le32(ir); ++ ref.seq = cpu_to_le16(ir); ++ ++ i = ntfs_iget5(sb, &ref, NULL); ++ if (IS_ERR(i)) { ++next: ++ ntfs_notice( ++ sb, ++ "Invalid reserved record %x", ++ ref.low); ++ continue; ++ } ++ if (is_bad_inode(i)) { ++ iput(i); ++ goto next; ++ } ++ ++ ni = ntfs_i(i); ++ ++ mrec = ni->mi.mrec; ++ ++ if (!is_rec_base(mrec)) ++ goto next; ++ ++ if (mrec->hard_links) ++ goto next; ++ ++ if (!ni_std(ni)) ++ goto next; ++ ++ if (ni_find_attr(ni, NULL, NULL, ATTR_NAME, ++ NULL, 0, NULL, NULL)) ++ goto next; ++ ++ __clear_bit(ir - MFT_REC_RESERVED, ++ &sbi->mft.reserved_bitmap); ++ } ++ } ++ ++ /* Scan 5 bits for zero. Bit 0 == MFT_REC_RESERVED */ ++ zbit = find_next_zero_bit(&sbi->mft.reserved_bitmap, ++ MFT_REC_FREE, MFT_REC_RESERVED); ++ if (zbit >= MFT_REC_FREE) { ++ sbi->mft.next_reserved = MFT_REC_FREE; ++ goto out; ++ } ++ ++ zlen = 1; ++ sbi->mft.next_reserved = zbit; ++ } else { ++reserve_mft: ++ zlen = zbit == MFT_REC_FREE ? (MFT_REC_USER - MFT_REC_FREE) : 4; ++ if (zbit + zlen > wnd->nbits) ++ zlen = wnd->nbits - zbit; ++ ++ while (zlen > 1 && !wnd_is_free(wnd, zbit, zlen)) ++ zlen -= 1; ++ ++ /* [zbit, zbit + zlen) will be used for Mft itself */ ++ from = sbi->mft.used; ++ if (from < zbit) ++ from = zbit; ++ to = zbit + zlen; ++ if (from < to) { ++ ntfs_clear_mft_tail(sbi, from, to); ++ sbi->mft.used = to; ++ } ++ } ++ ++ if (mft) { ++ *rno = zbit; ++ zbit += 1; ++ zlen -= 1; ++ } ++ ++ wnd_zone_set(wnd, zbit, zlen); ++ ++found: ++ if (!mft) { ++ /* The request to get record for general purpose */ ++ if (sbi->mft.next_free < MFT_REC_USER) ++ sbi->mft.next_free = MFT_REC_USER; ++ ++ for (;;) { ++ if (sbi->mft.next_free >= sbi->mft.bitmap.nbits) { ++ } else if (!wnd_find(wnd, 1, MFT_REC_USER, 0, &fr)) { ++ sbi->mft.next_free = sbi->mft.bitmap.nbits; ++ } else { ++ *rno = fr; ++ sbi->mft.next_free = *rno + 1; ++ break; ++ } ++ ++ err = ntfs_extend_mft(sbi); ++ if (err) ++ goto out; ++ } ++ } ++ ++ if (ni && !ni_add_subrecord(ni, *rno, mi)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* We have found a record that are not reserved for next MFT */ ++ if (*rno >= MFT_REC_FREE) ++ wnd_set_used(wnd, *rno, 1); ++ else if (*rno >= MFT_REC_RESERVED && sbi->mft.reserved_bitmap_inited) ++ __set_bit(*rno - MFT_REC_RESERVED, &sbi->mft.reserved_bitmap); ++ ++out: ++ if (!mft) ++ up_write(&wnd->rw_lock); ++ ++ return err; ++} ++ ++/* ++ * ntfs_mark_rec_free ++ * ++ * marks record as free ++ */ ++void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno) ++{ ++ struct wnd_bitmap *wnd = &sbi->mft.bitmap; ++ ++ down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT); ++ if (rno >= wnd->nbits) ++ goto out; ++ ++ if (rno >= MFT_REC_FREE) { ++ if (!wnd_is_used(wnd, rno, 1)) ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ else ++ wnd_set_free(wnd, rno, 1); ++ } else if (rno >= MFT_REC_RESERVED && sbi->mft.reserved_bitmap_inited) { ++ __clear_bit(rno - MFT_REC_RESERVED, &sbi->mft.reserved_bitmap); ++ } ++ ++ if (rno < wnd_zone_bit(wnd)) ++ wnd_zone_set(wnd, rno, 1); ++ else if (rno < sbi->mft.next_free && rno >= MFT_REC_USER) ++ sbi->mft.next_free = rno; ++ ++out: ++ up_write(&wnd->rw_lock); ++} ++ ++/* ++ * ntfs_clear_mft_tail ++ * ++ * formats empty records [from, to) ++ * sbi->mft.bitmap is locked for write ++ */ ++int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to) ++{ ++ int err; ++ u32 rs; ++ u64 vbo; ++ struct runs_tree *run; ++ struct ntfs_inode *ni; ++ ++ if (from >= to) ++ return 0; ++ ++ rs = sbi->record_size; ++ ni = sbi->mft.ni; ++ run = &ni->file.run; ++ ++ down_read(&ni->file.run_lock); ++ vbo = (u64)from * rs; ++ for (; from < to; from++, vbo += rs) { ++ struct ntfs_buffers nb; ++ ++ err = ntfs_get_bh(sbi, run, vbo, rs, &nb); ++ if (err) ++ goto out; ++ ++ err = ntfs_write_bh(sbi, &sbi->new_rec->rhdr, &nb, 0); ++ nb_put(&nb); ++ if (err) ++ goto out; ++ } ++ ++out: ++ sbi->mft.used = from; ++ up_read(&ni->file.run_lock); ++ return err; ++} ++ ++/* ++ * ntfs_refresh_zone ++ * ++ * refreshes Mft zone ++ * sbi->used.bitmap is locked for rw ++ * sbi->mft.bitmap is locked for write ++ * sbi->mft.ni->file.run_lock for write ++ */ ++int ntfs_refresh_zone(struct ntfs_sb_info *sbi) ++{ ++ CLST zone_limit, zone_max, lcn, vcn, len; ++ size_t lcn_s, zlen; ++ struct wnd_bitmap *wnd = &sbi->used.bitmap; ++ struct ntfs_inode *ni = sbi->mft.ni; ++ ++ /* Do not change anything unless we have non empty Mft zone */ ++ if (wnd_zone_len(wnd)) ++ return 0; ++ ++ /* ++ * Compute the mft zone at two steps ++ * It would be nice if we are able to allocate ++ * 1/8 of total clusters for MFT but not more then 512 MB ++ */ ++ zone_limit = (512 * 1024 * 1024) >> sbi->cluster_bits; ++ zone_max = wnd->nbits >> 3; ++ if (zone_max > zone_limit) ++ zone_max = zone_limit; ++ ++ vcn = bytes_to_cluster(sbi, ++ (u64)sbi->mft.bitmap.nbits << sbi->record_bits); ++ ++ if (!run_lookup_entry(&ni->file.run, vcn - 1, &lcn, &len, NULL)) ++ lcn = SPARSE_LCN; ++ ++ /* We should always find Last Lcn for MFT */ ++ if (lcn == SPARSE_LCN) ++ return -EINVAL; ++ ++ lcn_s = lcn + 1; ++ ++ /* Try to allocate clusters after last MFT run */ ++ zlen = wnd_find(wnd, zone_max, lcn_s, 0, &lcn_s); ++ if (!zlen) { ++ ntfs_notice(sbi->sb, "MftZone: unavailable"); ++ return 0; ++ } ++ ++ /* Truncate too large zone */ ++ wnd_zone_set(wnd, lcn_s, zlen); ++ ++ return 0; ++} ++ ++/* ++ * ntfs_update_mftmirr ++ * ++ * updates $MFTMirr data ++ */ ++int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) ++{ ++ int err; ++ struct super_block *sb = sbi->sb; ++ u32 blocksize = sb->s_blocksize; ++ sector_t block1, block2; ++ u32 bytes; ++ ++ if (!(sbi->flags & NTFS_FLAGS_MFTMIRR)) ++ return 0; ++ ++ err = 0; ++ bytes = sbi->mft.recs_mirr << sbi->record_bits; ++ block1 = sbi->mft.lbo >> sb->s_blocksize_bits; ++ block2 = sbi->mft.lbo2 >> sb->s_blocksize_bits; ++ ++ for (; bytes >= blocksize; bytes -= blocksize) { ++ struct buffer_head *bh1, *bh2; ++ ++ bh1 = sb_bread(sb, block1++); ++ if (!bh1) { ++ err = -EIO; ++ goto out; ++ } ++ ++ bh2 = sb_getblk(sb, block2++); ++ if (!bh2) { ++ put_bh(bh1); ++ err = -EIO; ++ goto out; ++ } ++ ++ if (buffer_locked(bh2)) ++ __wait_on_buffer(bh2); ++ ++ lock_buffer(bh2); ++ memcpy(bh2->b_data, bh1->b_data, blocksize); ++ set_buffer_uptodate(bh2); ++ mark_buffer_dirty(bh2); ++ unlock_buffer(bh2); ++ ++ put_bh(bh1); ++ bh1 = NULL; ++ ++ if (wait) ++ err = sync_dirty_buffer(bh2); ++ ++ put_bh(bh2); ++ if (err) ++ goto out; ++ } ++ ++ sbi->flags &= ~NTFS_FLAGS_MFTMIRR; ++ ++out: ++ return err; ++} ++ ++/* ++ * ntfs_set_state ++ * ++ * mount: ntfs_set_state(NTFS_DIRTY_DIRTY) ++ * umount: ntfs_set_state(NTFS_DIRTY_CLEAR) ++ * ntfs error: ntfs_set_state(NTFS_DIRTY_ERROR) ++ */ ++int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) ++{ ++ int err; ++ struct ATTRIB *attr; ++ struct VOLUME_INFO *info; ++ struct mft_inode *mi; ++ struct ntfs_inode *ni; ++ ++ /* ++ * do not change state if fs was real_dirty ++ * do not change state if fs already dirty(clear) ++ * do not change any thing if mounted read only ++ */ ++ if (sbi->volume.real_dirty || sb_rdonly(sbi->sb)) ++ return 0; ++ ++ /* Check cached value */ ++ if ((dirty == NTFS_DIRTY_CLEAR ? 0 : VOLUME_FLAG_DIRTY) == ++ (sbi->volume.flags & VOLUME_FLAG_DIRTY)) ++ return 0; ++ ++ ni = sbi->volume.ni; ++ if (!ni) ++ return -EINVAL; ++ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_DIRTY); ++ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_VOL_INFO, NULL, 0, NULL, &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ info = resident_data_ex(attr, SIZEOF_ATTRIBUTE_VOLUME_INFO); ++ if (!info) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ switch (dirty) { ++ case NTFS_DIRTY_ERROR: ++ ntfs_notice(sbi->sb, "Mark volume as dirty due to NTFS errors"); ++ sbi->volume.real_dirty = true; ++ fallthrough; ++ case NTFS_DIRTY_DIRTY: ++ info->flags |= VOLUME_FLAG_DIRTY; ++ break; ++ case NTFS_DIRTY_CLEAR: ++ info->flags &= ~VOLUME_FLAG_DIRTY; ++ break; ++ } ++ /* cache current volume flags*/ ++ sbi->volume.flags = info->flags; ++ mi->dirty = true; ++ err = 0; ++ ++out: ++ ni_unlock(ni); ++ if (err) ++ return err; ++ ++ mark_inode_dirty(&ni->vfs_inode); ++ /*verify(!ntfs_update_mftmirr()); */ ++ ++ /* ++ * if we used wait=1, sync_inode_metadata waits for the io for the ++ * inode to finish. It hangs when media is removed. ++ * So wait=0 is sent down to sync_inode_metadata ++ * and filemap_fdatawrite is used for the data blocks ++ */ ++ err = sync_inode_metadata(&ni->vfs_inode, 0); ++ if (!err) ++ err = filemap_fdatawrite(ni->vfs_inode.i_mapping); ++ ++ return err; ++} ++ ++/* ++ * security_hash ++ * ++ * calculates a hash of security descriptor ++ */ ++static inline __le32 security_hash(const void *sd, size_t bytes) ++{ ++ u32 hash = 0; ++ const __le32 *ptr = sd; ++ ++ bytes >>= 2; ++ while (bytes--) ++ hash = ((hash >> 0x1D) | (hash << 3)) + le32_to_cpu(*ptr++); ++ return cpu_to_le32(hash); ++} ++ ++int ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer) ++{ ++ struct block_device *bdev = sb->s_bdev; ++ u32 blocksize = sb->s_blocksize; ++ u64 block = lbo >> sb->s_blocksize_bits; ++ u32 off = lbo & (blocksize - 1); ++ u32 op = blocksize - off; ++ ++ for (; bytes; block += 1, off = 0, op = blocksize) { ++ struct buffer_head *bh = __bread(bdev, block, blocksize); ++ ++ if (!bh) ++ return -EIO; ++ ++ if (op > bytes) ++ op = bytes; ++ ++ memcpy(buffer, bh->b_data + off, op); ++ ++ put_bh(bh); ++ ++ bytes -= op; ++ buffer = Add2Ptr(buffer, op); ++ } ++ ++ return 0; ++} ++ ++int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes, ++ const void *buf, int wait) ++{ ++ u32 blocksize = sb->s_blocksize; ++ struct block_device *bdev = sb->s_bdev; ++ sector_t block = lbo >> sb->s_blocksize_bits; ++ u32 off = lbo & (blocksize - 1); ++ u32 op = blocksize - off; ++ struct buffer_head *bh; ++ ++ if (!wait && (sb->s_flags & SB_SYNCHRONOUS)) ++ wait = 1; ++ ++ for (; bytes; block += 1, off = 0, op = blocksize) { ++ if (op > bytes) ++ op = bytes; ++ ++ if (op < blocksize) { ++ bh = __bread(bdev, block, blocksize); ++ if (!bh) { ++ ntfs_err(sb, "failed to read block %llx", ++ (u64)block); ++ return -EIO; ++ } ++ } else { ++ bh = __getblk(bdev, block, blocksize); ++ if (!bh) ++ return -ENOMEM; ++ } ++ ++ if (buffer_locked(bh)) ++ __wait_on_buffer(bh); ++ ++ lock_buffer(bh); ++ if (buf) { ++ memcpy(bh->b_data + off, buf, op); ++ buf = Add2Ptr(buf, op); ++ } else { ++ memset(bh->b_data + off, -1, op); ++ } ++ ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ unlock_buffer(bh); ++ ++ if (wait) { ++ int err = sync_dirty_buffer(bh); ++ ++ if (err) { ++ ntfs_err( ++ sb, ++ "failed to sync buffer at block %llx, error %d", ++ (u64)block, err); ++ put_bh(bh); ++ return err; ++ } ++ } ++ ++ put_bh(bh); ++ ++ bytes -= op; ++ } ++ return 0; ++} ++ ++int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ u64 vbo, const void *buf, size_t bytes) ++{ ++ struct super_block *sb = sbi->sb; ++ u8 cluster_bits = sbi->cluster_bits; ++ u32 off = vbo & sbi->cluster_mask; ++ CLST lcn, clen, vcn = vbo >> cluster_bits, vcn_next; ++ u64 lbo, len; ++ size_t idx; ++ ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) ++ return -ENOENT; ++ ++ if (lcn == SPARSE_LCN) ++ return -EINVAL; ++ ++ lbo = ((u64)lcn << cluster_bits) + off; ++ len = ((u64)clen << cluster_bits) - off; ++ ++ for (;;) { ++ u32 op = len < bytes ? len : bytes; ++ int err = ntfs_sb_write(sb, lbo, op, buf, 0); ++ ++ if (err) ++ return err; ++ ++ bytes -= op; ++ if (!bytes) ++ break; ++ ++ vcn_next = vcn + clen; ++ if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || ++ vcn != vcn_next) ++ return -ENOENT; ++ ++ if (lcn == SPARSE_LCN) ++ return -EINVAL; ++ ++ if (buf) ++ buf = Add2Ptr(buf, op); ++ ++ lbo = ((u64)lcn << cluster_bits); ++ len = ((u64)clen << cluster_bits); ++ } ++ ++ return 0; ++} ++ ++struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi, ++ const struct runs_tree *run, u64 vbo) ++{ ++ struct super_block *sb = sbi->sb; ++ u8 cluster_bits = sbi->cluster_bits; ++ CLST lcn; ++ u64 lbo; ++ ++ if (!run_lookup_entry(run, vbo >> cluster_bits, &lcn, NULL, NULL)) ++ return ERR_PTR(-ENOENT); ++ ++ lbo = ((u64)lcn << cluster_bits) + (vbo & sbi->cluster_mask); ++ ++ return ntfs_bread(sb, lbo >> sb->s_blocksize_bits); ++} ++ ++int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb) ++{ ++ int err; ++ struct super_block *sb = sbi->sb; ++ u32 blocksize = sb->s_blocksize; ++ u8 cluster_bits = sbi->cluster_bits; ++ u32 off = vbo & sbi->cluster_mask; ++ u32 nbh = 0; ++ CLST vcn_next, vcn = vbo >> cluster_bits; ++ CLST lcn, clen; ++ u64 lbo, len; ++ size_t idx; ++ struct buffer_head *bh; ++ ++ if (!run) { ++ /* first reading of $Volume + $MFTMirr + LogFile goes here*/ ++ if (vbo > MFT_REC_VOL * sbi->record_size) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ /* use absolute boot's 'MFTCluster' to read record */ ++ lbo = vbo + sbi->mft.lbo; ++ len = sbi->record_size; ++ } else if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { ++ err = -ENOENT; ++ goto out; ++ } else { ++ if (lcn == SPARSE_LCN) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ lbo = ((u64)lcn << cluster_bits) + off; ++ len = ((u64)clen << cluster_bits) - off; ++ } ++ ++ off = lbo & (blocksize - 1); ++ if (nb) { ++ nb->off = off; ++ nb->bytes = bytes; ++ } ++ ++ for (;;) { ++ u32 len32 = len >= bytes ? bytes : len; ++ sector_t block = lbo >> sb->s_blocksize_bits; ++ ++ do { ++ u32 op = blocksize - off; ++ ++ if (op > len32) ++ op = len32; ++ ++ bh = ntfs_bread(sb, block); ++ if (!bh) { ++ err = -EIO; ++ goto out; ++ } ++ ++ if (buf) { ++ memcpy(buf, bh->b_data + off, op); ++ buf = Add2Ptr(buf, op); ++ } ++ ++ if (!nb) { ++ put_bh(bh); ++ } else if (nbh >= ARRAY_SIZE(nb->bh)) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ nb->bh[nbh++] = bh; ++ nb->nbufs = nbh; ++ } ++ ++ bytes -= op; ++ if (!bytes) ++ return 0; ++ len32 -= op; ++ block += 1; ++ off = 0; ++ ++ } while (len32); ++ ++ vcn_next = vcn + clen; ++ if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || ++ vcn != vcn_next) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ if (lcn == SPARSE_LCN) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ lbo = ((u64)lcn << cluster_bits); ++ len = ((u64)clen << cluster_bits); ++ } ++ ++out: ++ if (!nbh) ++ return err; ++ ++ while (nbh) { ++ put_bh(nb->bh[--nbh]); ++ nb->bh[nbh] = NULL; ++ } ++ ++ nb->nbufs = 0; ++ return err; ++} ++ ++/* Returns < 0 if error, 0 if ok, '-E_NTFS_FIXUP' if need to update fixups */ ++int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, ++ struct NTFS_RECORD_HEADER *rhdr, u32 bytes, ++ struct ntfs_buffers *nb) ++{ ++ int err = ntfs_read_run_nb(sbi, run, vbo, rhdr, bytes, nb); ++ ++ if (err) ++ return err; ++ return ntfs_fix_post_read(rhdr, nb->bytes, true); ++} ++ ++int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, ++ u32 bytes, struct ntfs_buffers *nb) ++{ ++ int err = 0; ++ struct super_block *sb = sbi->sb; ++ u32 blocksize = sb->s_blocksize; ++ u8 cluster_bits = sbi->cluster_bits; ++ CLST vcn_next, vcn = vbo >> cluster_bits; ++ u32 off; ++ u32 nbh = 0; ++ CLST lcn, clen; ++ u64 lbo, len; ++ size_t idx; ++ ++ nb->bytes = bytes; ++ ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ off = vbo & sbi->cluster_mask; ++ lbo = ((u64)lcn << cluster_bits) + off; ++ len = ((u64)clen << cluster_bits) - off; ++ ++ nb->off = off = lbo & (blocksize - 1); ++ ++ for (;;) { ++ u32 len32 = len < bytes ? len : bytes; ++ sector_t block = lbo >> sb->s_blocksize_bits; ++ ++ do { ++ u32 op; ++ struct buffer_head *bh; ++ ++ if (nbh >= ARRAY_SIZE(nb->bh)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ op = blocksize - off; ++ if (op > len32) ++ op = len32; ++ ++ if (op == blocksize) { ++ bh = sb_getblk(sb, block); ++ if (!bh) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (buffer_locked(bh)) ++ __wait_on_buffer(bh); ++ set_buffer_uptodate(bh); ++ } else { ++ bh = ntfs_bread(sb, block); ++ if (!bh) { ++ err = -EIO; ++ goto out; ++ } ++ } ++ ++ nb->bh[nbh++] = bh; ++ bytes -= op; ++ if (!bytes) { ++ nb->nbufs = nbh; ++ return 0; ++ } ++ ++ block += 1; ++ len32 -= op; ++ off = 0; ++ } while (len32); ++ ++ vcn_next = vcn + clen; ++ if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || ++ vcn != vcn_next) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ lbo = ((u64)lcn << cluster_bits); ++ len = ((u64)clen << cluster_bits); ++ } ++ ++out: ++ while (nbh) { ++ put_bh(nb->bh[--nbh]); ++ nb->bh[nbh] = NULL; ++ } ++ ++ nb->nbufs = 0; ++ ++ return err; ++} ++ ++int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr, ++ struct ntfs_buffers *nb, int sync) ++{ ++ int err = 0; ++ struct super_block *sb = sbi->sb; ++ u32 block_size = sb->s_blocksize; ++ u32 bytes = nb->bytes; ++ u32 off = nb->off; ++ u16 fo = le16_to_cpu(rhdr->fix_off); ++ u16 fn = le16_to_cpu(rhdr->fix_num); ++ u32 idx; ++ __le16 *fixup; ++ __le16 sample; ++ ++ if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || ++ fn * SECTOR_SIZE > bytes) { ++ return -EINVAL; ++ } ++ ++ for (idx = 0; bytes && idx < nb->nbufs; idx += 1, off = 0) { ++ u32 op = block_size - off; ++ char *bh_data; ++ struct buffer_head *bh = nb->bh[idx]; ++ __le16 *ptr, *end_data; ++ ++ if (op > bytes) ++ op = bytes; ++ ++ if (buffer_locked(bh)) ++ __wait_on_buffer(bh); ++ ++ lock_buffer(nb->bh[idx]); ++ ++ bh_data = bh->b_data + off; ++ end_data = Add2Ptr(bh_data, op); ++ memcpy(bh_data, rhdr, op); ++ ++ if (!idx) { ++ u16 t16; ++ ++ fixup = Add2Ptr(bh_data, fo); ++ sample = *fixup; ++ t16 = le16_to_cpu(sample); ++ if (t16 >= 0x7FFF) { ++ sample = *fixup = cpu_to_le16(1); ++ } else { ++ sample = cpu_to_le16(t16 + 1); ++ *fixup = sample; ++ } ++ ++ *(__le16 *)Add2Ptr(rhdr, fo) = sample; ++ } ++ ++ ptr = Add2Ptr(bh_data, SECTOR_SIZE - sizeof(short)); ++ ++ do { ++ *++fixup = *ptr; ++ *ptr = sample; ++ ptr += SECTOR_SIZE / sizeof(short); ++ } while (ptr < end_data); ++ ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ unlock_buffer(bh); ++ ++ if (sync) { ++ int err2 = sync_dirty_buffer(bh); ++ ++ if (!err && err2) ++ err = err2; ++ } ++ ++ bytes -= op; ++ rhdr = Add2Ptr(rhdr, op); ++ } ++ ++ return err; ++} ++ ++static inline struct bio *ntfs_alloc_bio(u32 nr_vecs) ++{ ++ struct bio *bio = bio_alloc(GFP_NOFS | __GFP_HIGH, nr_vecs); ++ ++ if (!bio && (current->flags & PF_MEMALLOC)) { ++ while (!bio && (nr_vecs /= 2)) ++ bio = bio_alloc(GFP_NOFS | __GFP_HIGH, nr_vecs); ++ } ++ return bio; ++} ++ ++/* read/write pages from/to disk*/ ++int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ struct page **pages, u32 nr_pages, u64 vbo, u32 bytes, ++ u32 op) ++{ ++ int err = 0; ++ struct bio *new, *bio = NULL; ++ struct super_block *sb = sbi->sb; ++ struct block_device *bdev = sb->s_bdev; ++ struct page *page; ++ u8 cluster_bits = sbi->cluster_bits; ++ CLST lcn, clen, vcn, vcn_next; ++ u32 add, off, page_idx; ++ u64 lbo, len; ++ size_t run_idx; ++ struct blk_plug plug; ++ ++ if (!bytes) ++ return 0; ++ ++ blk_start_plug(&plug); ++ ++ /* align vbo and bytes to be 512 bytes aligned */ ++ lbo = (vbo + bytes + 511) & ~511ull; ++ vbo = vbo & ~511ull; ++ bytes = lbo - vbo; ++ ++ vcn = vbo >> cluster_bits; ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &run_idx)) { ++ err = -ENOENT; ++ goto out; ++ } ++ off = vbo & sbi->cluster_mask; ++ page_idx = 0; ++ page = pages[0]; ++ ++ for (;;) { ++ lbo = ((u64)lcn << cluster_bits) + off; ++ len = ((u64)clen << cluster_bits) - off; ++new_bio: ++ new = ntfs_alloc_bio(nr_pages - page_idx); ++ if (!new) { ++ err = -ENOMEM; ++ goto out; ++ } ++ if (bio) { ++ bio_chain(bio, new); ++ submit_bio(bio); ++ } ++ bio = new; ++ bio_set_dev(bio, bdev); ++ bio->bi_iter.bi_sector = lbo >> 9; ++ bio->bi_opf = op; ++ ++ while (len) { ++ off = vbo & (PAGE_SIZE - 1); ++ add = off + len > PAGE_SIZE ? (PAGE_SIZE - off) : len; ++ ++ if (bio_add_page(bio, page, add, off) < add) ++ goto new_bio; ++ ++ if (bytes <= add) ++ goto out; ++ bytes -= add; ++ vbo += add; ++ ++ if (add + off == PAGE_SIZE) { ++ page_idx += 1; ++ if (WARN_ON(page_idx >= nr_pages)) { ++ err = -EINVAL; ++ goto out; ++ } ++ page = pages[page_idx]; ++ } ++ ++ if (len <= add) ++ break; ++ len -= add; ++ lbo += add; ++ } ++ ++ vcn_next = vcn + clen; ++ if (!run_get_entry(run, ++run_idx, &vcn, &lcn, &clen) || ++ vcn != vcn_next) { ++ err = -ENOENT; ++ goto out; ++ } ++ off = 0; ++ } ++out: ++ if (bio) { ++ if (!err) ++ err = submit_bio_wait(bio); ++ bio_put(bio); ++ } ++ blk_finish_plug(&plug); ++ ++ return err; ++} ++ ++/* ++ * Helper for ntfs_loadlog_and_replay ++ * fill on-disk logfile range by (-1) ++ * this means empty logfile ++ */ ++int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run) ++{ ++ int err = 0; ++ struct super_block *sb = sbi->sb; ++ struct block_device *bdev = sb->s_bdev; ++ u8 cluster_bits = sbi->cluster_bits; ++ struct bio *new, *bio = NULL; ++ CLST lcn, clen; ++ u64 lbo, len; ++ size_t run_idx; ++ struct page *fill; ++ void *kaddr; ++ struct blk_plug plug; ++ ++ fill = alloc_page(GFP_KERNEL); ++ if (!fill) ++ return -ENOMEM; ++ ++ kaddr = kmap_atomic(fill); ++ memset(kaddr, -1, PAGE_SIZE); ++ kunmap_atomic(kaddr); ++ flush_dcache_page(fill); ++ lock_page(fill); ++ ++ if (!run_lookup_entry(run, 0, &lcn, &clen, &run_idx)) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ /* ++ * TODO: try blkdev_issue_write_same ++ */ ++ blk_start_plug(&plug); ++ do { ++ lbo = (u64)lcn << cluster_bits; ++ len = (u64)clen << cluster_bits; ++new_bio: ++ new = ntfs_alloc_bio(BIO_MAX_VECS); ++ if (!new) { ++ err = -ENOMEM; ++ break; ++ } ++ if (bio) { ++ bio_chain(bio, new); ++ submit_bio(bio); ++ } ++ bio = new; ++ bio_set_dev(bio, bdev); ++ bio->bi_opf = REQ_OP_WRITE; ++ bio->bi_iter.bi_sector = lbo >> 9; ++ ++ for (;;) { ++ u32 add = len > PAGE_SIZE ? PAGE_SIZE : len; ++ ++ if (bio_add_page(bio, fill, add, 0) < add) ++ goto new_bio; ++ ++ lbo += add; ++ if (len <= add) ++ break; ++ len -= add; ++ } ++ } while (run_get_entry(run, ++run_idx, NULL, &lcn, &clen)); ++ ++ if (bio) { ++ if (!err) ++ err = submit_bio_wait(bio); ++ bio_put(bio); ++ } ++ blk_finish_plug(&plug); ++out: ++ unlock_page(fill); ++ put_page(fill); ++ ++ return err; ++} ++ ++int ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run, ++ u64 vbo, u64 *lbo, u64 *bytes) ++{ ++ u32 off; ++ CLST lcn, len; ++ u8 cluster_bits = sbi->cluster_bits; ++ ++ if (!run_lookup_entry(run, vbo >> cluster_bits, &lcn, &len, NULL)) ++ return -ENOENT; ++ ++ off = vbo & sbi->cluster_mask; ++ *lbo = lcn == SPARSE_LCN ? -1 : (((u64)lcn << cluster_bits) + off); ++ *bytes = ((u64)len << cluster_bits) - off; ++ ++ return 0; ++} ++ ++struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, bool dir) ++{ ++ int err = 0; ++ struct super_block *sb = sbi->sb; ++ struct inode *inode = new_inode(sb); ++ struct ntfs_inode *ni; ++ ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ ++ ni = ntfs_i(inode); ++ ++ err = mi_format_new(&ni->mi, sbi, rno, dir ? RECORD_FLAG_DIR : 0, ++ false); ++ if (err) ++ goto out; ++ ++ inode->i_ino = rno; ++ if (insert_inode_locked(inode) < 0) { ++ err = -EIO; ++ goto out; ++ } ++ ++out: ++ if (err) { ++ iput(inode); ++ ni = ERR_PTR(err); ++ } ++ return ni; ++} ++ ++/* ++ * O:BAG:BAD:(A;OICI;FA;;;WD) ++ * owner S-1-5-32-544 (Administrators) ++ * group S-1-5-32-544 (Administrators) ++ * ACE: allow S-1-1-0 (Everyone) with FILE_ALL_ACCESS ++ */ ++const u8 s_default_security[] __aligned(8) = { ++ 0x01, 0x00, 0x04, 0x80, 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1C, 0x00, ++ 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x14, 0x00, 0xFF, 0x01, 0x1F, 0x00, ++ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ++ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, ++ 0x20, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, ++ 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, ++}; ++ ++static_assert(sizeof(s_default_security) == 0x50); ++ ++static inline u32 sid_length(const struct SID *sid) ++{ ++ return struct_size(sid, SubAuthority, sid->SubAuthorityCount); ++} ++ ++/* ++ * Thanks Mark Harmstone for idea ++ */ ++static bool is_acl_valid(const struct ACL *acl, u32 len) ++{ ++ const struct ACE_HEADER *ace; ++ u32 i; ++ u16 ace_count, ace_size; ++ ++ if (acl->AclRevision != ACL_REVISION && ++ acl->AclRevision != ACL_REVISION_DS) { ++ /* ++ * This value should be ACL_REVISION, unless the ACL contains an ++ * object-specific ACE, in which case this value must be ACL_REVISION_DS. ++ * All ACEs in an ACL must be at the same revision level. ++ */ ++ return false; ++ } ++ ++ if (acl->Sbz1) ++ return false; ++ ++ if (le16_to_cpu(acl->AclSize) > len) ++ return false; ++ ++ if (acl->Sbz2) ++ return false; ++ ++ len -= sizeof(struct ACL); ++ ace = (struct ACE_HEADER *)&acl[1]; ++ ace_count = le16_to_cpu(acl->AceCount); ++ ++ for (i = 0; i < ace_count; i++) { ++ if (len < sizeof(struct ACE_HEADER)) ++ return false; ++ ++ ace_size = le16_to_cpu(ace->AceSize); ++ if (len < ace_size) ++ return false; ++ ++ len -= ace_size; ++ ace = Add2Ptr(ace, ace_size); ++ } ++ ++ return true; ++} ++ ++bool is_sd_valid(const struct SECURITY_DESCRIPTOR_RELATIVE *sd, u32 len) ++{ ++ u32 sd_owner, sd_group, sd_sacl, sd_dacl; ++ ++ if (len < sizeof(struct SECURITY_DESCRIPTOR_RELATIVE)) ++ return false; ++ ++ if (sd->Revision != 1) ++ return false; ++ ++ if (sd->Sbz1) ++ return false; ++ ++ if (!(sd->Control & SE_SELF_RELATIVE)) ++ return false; ++ ++ sd_owner = le32_to_cpu(sd->Owner); ++ if (sd_owner) { ++ const struct SID *owner = Add2Ptr(sd, sd_owner); ++ ++ if (sd_owner + offsetof(struct SID, SubAuthority) > len) ++ return false; ++ ++ if (owner->Revision != 1) ++ return false; ++ ++ if (sd_owner + sid_length(owner) > len) ++ return false; ++ } ++ ++ sd_group = le32_to_cpu(sd->Group); ++ if (sd_group) { ++ const struct SID *group = Add2Ptr(sd, sd_group); ++ ++ if (sd_group + offsetof(struct SID, SubAuthority) > len) ++ return false; ++ ++ if (group->Revision != 1) ++ return false; ++ ++ if (sd_group + sid_length(group) > len) ++ return false; ++ } ++ ++ sd_sacl = le32_to_cpu(sd->Sacl); ++ if (sd_sacl) { ++ const struct ACL *sacl = Add2Ptr(sd, sd_sacl); ++ ++ if (sd_sacl + sizeof(struct ACL) > len) ++ return false; ++ ++ if (!is_acl_valid(sacl, len - sd_sacl)) ++ return false; ++ } ++ ++ sd_dacl = le32_to_cpu(sd->Dacl); ++ if (sd_dacl) { ++ const struct ACL *dacl = Add2Ptr(sd, sd_dacl); ++ ++ if (sd_dacl + sizeof(struct ACL) > len) ++ return false; ++ ++ if (!is_acl_valid(dacl, len - sd_dacl)) ++ return false; ++ } ++ ++ return true; ++} ++ ++/* ++ * ntfs_security_init ++ * ++ * loads and parse $Secure ++ */ ++int ntfs_security_init(struct ntfs_sb_info *sbi) ++{ ++ int err; ++ struct super_block *sb = sbi->sb; ++ struct inode *inode; ++ struct ntfs_inode *ni; ++ struct MFT_REF ref; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ u64 sds_size; ++ size_t cnt, off; ++ struct NTFS_DE *ne; ++ struct NTFS_DE_SII *sii_e; ++ struct ntfs_fnd *fnd_sii = NULL; ++ const struct INDEX_ROOT *root_sii; ++ const struct INDEX_ROOT *root_sdh; ++ struct ntfs_index *indx_sdh = &sbi->security.index_sdh; ++ struct ntfs_index *indx_sii = &sbi->security.index_sii; ++ ++ ref.low = cpu_to_le32(MFT_REC_SECURE); ++ ref.high = 0; ++ ref.seq = cpu_to_le16(MFT_REC_SECURE); ++ ++ inode = ntfs_iget5(sb, &ref, &NAME_SECURE); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $Secure."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ le = NULL; ++ ++ attr = ni_find_attr(ni, NULL, &le, ATTR_ROOT, SDH_NAME, ++ ARRAY_SIZE(SDH_NAME), NULL, NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ root_sdh = resident_data(attr); ++ if (root_sdh->type != ATTR_ZERO || ++ root_sdh->rule != NTFS_COLLATION_TYPE_SECURITY_HASH) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = indx_init(indx_sdh, sbi, attr, INDEX_MUTEX_SDH); ++ if (err) ++ goto out; ++ ++ attr = ni_find_attr(ni, attr, &le, ATTR_ROOT, SII_NAME, ++ ARRAY_SIZE(SII_NAME), NULL, NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ root_sii = resident_data(attr); ++ if (root_sii->type != ATTR_ZERO || ++ root_sii->rule != NTFS_COLLATION_TYPE_UINT) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = indx_init(indx_sii, sbi, attr, INDEX_MUTEX_SII); ++ if (err) ++ goto out; ++ ++ fnd_sii = fnd_get(); ++ if (!fnd_sii) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ sds_size = inode->i_size; ++ ++ /* Find the last valid Id */ ++ sbi->security.next_id = SECURITY_ID_FIRST; ++ /* Always write new security at the end of bucket */ ++ sbi->security.next_off = ++ Quad2Align(sds_size - SecurityDescriptorsBlockSize); ++ ++ cnt = 0; ++ off = 0; ++ ne = NULL; ++ ++ for (;;) { ++ u32 next_id; ++ ++ err = indx_find_raw(indx_sii, ni, root_sii, &ne, &off, fnd_sii); ++ if (err || !ne) ++ break; ++ ++ sii_e = (struct NTFS_DE_SII *)ne; ++ if (le16_to_cpu(ne->view.data_size) < SIZEOF_SECURITY_HDR) ++ continue; ++ ++ next_id = le32_to_cpu(sii_e->sec_id) + 1; ++ if (next_id >= sbi->security.next_id) ++ sbi->security.next_id = next_id; ++ ++ cnt += 1; ++ } ++ ++ sbi->security.ni = ni; ++ inode = NULL; ++out: ++ iput(inode); ++ fnd_put(fnd_sii); ++ ++ return err; ++} ++ ++/* ++ * ntfs_get_security_by_id ++ * ++ * reads security descriptor by id ++ */ ++int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, ++ struct SECURITY_DESCRIPTOR_RELATIVE **sd, ++ size_t *size) ++{ ++ int err; ++ int diff; ++ struct ntfs_inode *ni = sbi->security.ni; ++ struct ntfs_index *indx = &sbi->security.index_sii; ++ void *p = NULL; ++ struct NTFS_DE_SII *sii_e; ++ struct ntfs_fnd *fnd_sii; ++ struct SECURITY_HDR d_security; ++ const struct INDEX_ROOT *root_sii; ++ u32 t32; ++ ++ *sd = NULL; ++ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_SECURITY); ++ ++ fnd_sii = fnd_get(); ++ if (!fnd_sii) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ root_sii = indx_get_root(indx, ni, NULL, NULL); ++ if (!root_sii) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Try to find this SECURITY descriptor in SII indexes */ ++ err = indx_find(indx, ni, root_sii, &security_id, sizeof(security_id), ++ NULL, &diff, (struct NTFS_DE **)&sii_e, fnd_sii); ++ if (err) ++ goto out; ++ ++ if (diff) ++ goto out; ++ ++ t32 = le32_to_cpu(sii_e->sec_hdr.size); ++ if (t32 < SIZEOF_SECURITY_HDR) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (t32 > SIZEOF_SECURITY_HDR + 0x10000) { ++ /* ++ * looks like too big security. 0x10000 - is arbitrary big number ++ */ ++ err = -EFBIG; ++ goto out; ++ } ++ ++ *size = t32 - SIZEOF_SECURITY_HDR; ++ ++ p = ntfs_malloc(*size); ++ if (!p) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = ntfs_read_run_nb(sbi, &ni->file.run, ++ le64_to_cpu(sii_e->sec_hdr.off), &d_security, ++ sizeof(d_security), NULL); ++ if (err) ++ goto out; ++ ++ if (memcmp(&d_security, &sii_e->sec_hdr, SIZEOF_SECURITY_HDR)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = ntfs_read_run_nb(sbi, &ni->file.run, ++ le64_to_cpu(sii_e->sec_hdr.off) + ++ SIZEOF_SECURITY_HDR, ++ p, *size, NULL); ++ if (err) ++ goto out; ++ ++ *sd = p; ++ p = NULL; ++ ++out: ++ ntfs_free(p); ++ fnd_put(fnd_sii); ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++/* ++ * ntfs_insert_security ++ * ++ * inserts security descriptor into $Secure::SDS ++ * ++ * SECURITY Descriptor Stream data is organized into chunks of 256K bytes ++ * and it contains a mirror copy of each security descriptor. When writing ++ * to a security descriptor at location X, another copy will be written at ++ * location (X+256K). ++ * When writing a security descriptor that will cross the 256K boundary, ++ * the pointer will be advanced by 256K to skip ++ * over the mirror portion. ++ */ ++int ntfs_insert_security(struct ntfs_sb_info *sbi, ++ const struct SECURITY_DESCRIPTOR_RELATIVE *sd, ++ u32 size_sd, __le32 *security_id, bool *inserted) ++{ ++ int err, diff; ++ struct ntfs_inode *ni = sbi->security.ni; ++ struct ntfs_index *indx_sdh = &sbi->security.index_sdh; ++ struct ntfs_index *indx_sii = &sbi->security.index_sii; ++ struct NTFS_DE_SDH *e; ++ struct NTFS_DE_SDH sdh_e; ++ struct NTFS_DE_SII sii_e; ++ struct SECURITY_HDR *d_security; ++ u32 new_sec_size = size_sd + SIZEOF_SECURITY_HDR; ++ u32 aligned_sec_size = Quad2Align(new_sec_size); ++ struct SECURITY_KEY hash_key; ++ struct ntfs_fnd *fnd_sdh = NULL; ++ const struct INDEX_ROOT *root_sdh; ++ const struct INDEX_ROOT *root_sii; ++ u64 mirr_off, new_sds_size; ++ u32 next, left; ++ ++ static_assert((1 << Log2OfSecurityDescriptorsBlockSize) == ++ SecurityDescriptorsBlockSize); ++ ++ hash_key.hash = security_hash(sd, size_sd); ++ hash_key.sec_id = SECURITY_ID_INVALID; ++ ++ if (inserted) ++ *inserted = false; ++ *security_id = SECURITY_ID_INVALID; ++ ++ /* Allocate a temporal buffer*/ ++ d_security = ntfs_zalloc(aligned_sec_size); ++ if (!d_security) ++ return -ENOMEM; ++ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_SECURITY); ++ ++ fnd_sdh = fnd_get(); ++ if (!fnd_sdh) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ root_sdh = indx_get_root(indx_sdh, ni, NULL, NULL); ++ if (!root_sdh) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ root_sii = indx_get_root(indx_sii, ni, NULL, NULL); ++ if (!root_sii) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Check if such security already exists ++ * use "SDH" and hash -> to get the offset in "SDS" ++ */ ++ err = indx_find(indx_sdh, ni, root_sdh, &hash_key, sizeof(hash_key), ++ &d_security->key.sec_id, &diff, (struct NTFS_DE **)&e, ++ fnd_sdh); ++ if (err) ++ goto out; ++ ++ while (e) { ++ if (le32_to_cpu(e->sec_hdr.size) == new_sec_size) { ++ err = ntfs_read_run_nb(sbi, &ni->file.run, ++ le64_to_cpu(e->sec_hdr.off), ++ d_security, new_sec_size, NULL); ++ if (err) ++ goto out; ++ ++ if (le32_to_cpu(d_security->size) == new_sec_size && ++ d_security->key.hash == hash_key.hash && ++ !memcmp(d_security + 1, sd, size_sd)) { ++ *security_id = d_security->key.sec_id; ++ /*such security already exists*/ ++ err = 0; ++ goto out; ++ } ++ } ++ ++ err = indx_find_sort(indx_sdh, ni, root_sdh, ++ (struct NTFS_DE **)&e, fnd_sdh); ++ if (err) ++ goto out; ++ ++ if (!e || e->key.hash != hash_key.hash) ++ break; ++ } ++ ++ /* Zero unused space */ ++ next = sbi->security.next_off & (SecurityDescriptorsBlockSize - 1); ++ left = SecurityDescriptorsBlockSize - next; ++ ++ /* Zero gap until SecurityDescriptorsBlockSize */ ++ if (left < new_sec_size) { ++ /* zero "left" bytes from sbi->security.next_off */ ++ sbi->security.next_off += SecurityDescriptorsBlockSize + left; ++ } ++ ++ /* Zero tail of previous security */ ++ //used = ni->vfs_inode.i_size & (SecurityDescriptorsBlockSize - 1); ++ ++ /* ++ * Example: ++ * 0x40438 == ni->vfs_inode.i_size ++ * 0x00440 == sbi->security.next_off ++ * need to zero [0x438-0x440) ++ * if (next > used) { ++ * u32 tozero = next - used; ++ * zero "tozero" bytes from sbi->security.next_off - tozero ++ */ ++ ++ /* format new security descriptor */ ++ d_security->key.hash = hash_key.hash; ++ d_security->key.sec_id = cpu_to_le32(sbi->security.next_id); ++ d_security->off = cpu_to_le64(sbi->security.next_off); ++ d_security->size = cpu_to_le32(new_sec_size); ++ memcpy(d_security + 1, sd, size_sd); ++ ++ /* Write main SDS bucket */ ++ err = ntfs_sb_write_run(sbi, &ni->file.run, sbi->security.next_off, ++ d_security, aligned_sec_size); ++ ++ if (err) ++ goto out; ++ ++ mirr_off = sbi->security.next_off + SecurityDescriptorsBlockSize; ++ new_sds_size = mirr_off + aligned_sec_size; ++ ++ if (new_sds_size > ni->vfs_inode.i_size) { ++ err = attr_set_size(ni, ATTR_DATA, SDS_NAME, ++ ARRAY_SIZE(SDS_NAME), &ni->file.run, ++ new_sds_size, &new_sds_size, false, NULL); ++ if (err) ++ goto out; ++ } ++ ++ /* Write copy SDS bucket */ ++ err = ntfs_sb_write_run(sbi, &ni->file.run, mirr_off, d_security, ++ aligned_sec_size); ++ if (err) ++ goto out; ++ ++ /* Fill SII entry */ ++ sii_e.de.view.data_off = ++ cpu_to_le16(offsetof(struct NTFS_DE_SII, sec_hdr)); ++ sii_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR); ++ sii_e.de.view.res = 0; ++ sii_e.de.size = cpu_to_le16(SIZEOF_SII_DIRENTRY); ++ sii_e.de.key_size = cpu_to_le16(sizeof(d_security->key.sec_id)); ++ sii_e.de.flags = 0; ++ sii_e.de.res = 0; ++ sii_e.sec_id = d_security->key.sec_id; ++ memcpy(&sii_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR); ++ ++ err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL); ++ if (err) ++ goto out; ++ ++ /* Fill SDH entry */ ++ sdh_e.de.view.data_off = ++ cpu_to_le16(offsetof(struct NTFS_DE_SDH, sec_hdr)); ++ sdh_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR); ++ sdh_e.de.view.res = 0; ++ sdh_e.de.size = cpu_to_le16(SIZEOF_SDH_DIRENTRY); ++ sdh_e.de.key_size = cpu_to_le16(sizeof(sdh_e.key)); ++ sdh_e.de.flags = 0; ++ sdh_e.de.res = 0; ++ sdh_e.key.hash = d_security->key.hash; ++ sdh_e.key.sec_id = d_security->key.sec_id; ++ memcpy(&sdh_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR); ++ sdh_e.magic[0] = cpu_to_le16('I'); ++ sdh_e.magic[1] = cpu_to_le16('I'); ++ ++ fnd_clear(fnd_sdh); ++ err = indx_insert_entry(indx_sdh, ni, &sdh_e.de, (void *)(size_t)1, ++ fnd_sdh); ++ if (err) ++ goto out; ++ ++ *security_id = d_security->key.sec_id; ++ if (inserted) ++ *inserted = true; ++ ++ /* Update Id and offset for next descriptor */ ++ sbi->security.next_id += 1; ++ sbi->security.next_off += aligned_sec_size; ++ ++out: ++ fnd_put(fnd_sdh); ++ mark_inode_dirty(&ni->vfs_inode); ++ ni_unlock(ni); ++ ntfs_free(d_security); ++ ++ return err; ++} ++ ++/* ++ * ntfs_reparse_init ++ * ++ * loads and parse $Extend/$Reparse ++ */ ++int ntfs_reparse_init(struct ntfs_sb_info *sbi) ++{ ++ int err; ++ struct ntfs_inode *ni = sbi->reparse.ni; ++ struct ntfs_index *indx = &sbi->reparse.index_r; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ const struct INDEX_ROOT *root_r; ++ ++ if (!ni) ++ return 0; ++ ++ le = NULL; ++ attr = ni_find_attr(ni, NULL, &le, ATTR_ROOT, SR_NAME, ++ ARRAY_SIZE(SR_NAME), NULL, NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ root_r = resident_data(attr); ++ if (root_r->type != ATTR_ZERO || ++ root_r->rule != NTFS_COLLATION_TYPE_UINTS) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = indx_init(indx, sbi, attr, INDEX_MUTEX_SR); ++ if (err) ++ goto out; ++ ++out: ++ return err; ++} ++ ++/* ++ * ntfs_objid_init ++ * ++ * loads and parse $Extend/$ObjId ++ */ ++int ntfs_objid_init(struct ntfs_sb_info *sbi) ++{ ++ int err; ++ struct ntfs_inode *ni = sbi->objid.ni; ++ struct ntfs_index *indx = &sbi->objid.index_o; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ const struct INDEX_ROOT *root; ++ ++ if (!ni) ++ return 0; ++ ++ le = NULL; ++ attr = ni_find_attr(ni, NULL, &le, ATTR_ROOT, SO_NAME, ++ ARRAY_SIZE(SO_NAME), NULL, NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ root = resident_data(attr); ++ if (root->type != ATTR_ZERO || ++ root->rule != NTFS_COLLATION_TYPE_UINTS) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = indx_init(indx, sbi, attr, INDEX_MUTEX_SO); ++ if (err) ++ goto out; ++ ++out: ++ return err; ++} ++ ++int ntfs_objid_remove(struct ntfs_sb_info *sbi, struct GUID *guid) ++{ ++ int err; ++ struct ntfs_inode *ni = sbi->objid.ni; ++ struct ntfs_index *indx = &sbi->objid.index_o; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_OBJID); ++ ++ err = indx_delete_entry(indx, ni, guid, sizeof(*guid), NULL); ++ ++ mark_inode_dirty(&ni->vfs_inode); ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++int ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag, ++ const struct MFT_REF *ref) ++{ ++ int err; ++ struct ntfs_inode *ni = sbi->reparse.ni; ++ struct ntfs_index *indx = &sbi->reparse.index_r; ++ struct NTFS_DE_R re; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ memset(&re, 0, sizeof(re)); ++ ++ re.de.view.data_off = cpu_to_le16(offsetof(struct NTFS_DE_R, zero)); ++ re.de.size = cpu_to_le16(sizeof(struct NTFS_DE_R)); ++ re.de.key_size = cpu_to_le16(sizeof(re.key)); ++ ++ re.key.ReparseTag = rtag; ++ memcpy(&re.key.ref, ref, sizeof(*ref)); ++ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE); ++ ++ err = indx_insert_entry(indx, ni, &re.de, NULL, NULL); ++ ++ mark_inode_dirty(&ni->vfs_inode); ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag, ++ const struct MFT_REF *ref) ++{ ++ int err, diff; ++ struct ntfs_inode *ni = sbi->reparse.ni; ++ struct ntfs_index *indx = &sbi->reparse.index_r; ++ struct ntfs_fnd *fnd = NULL; ++ struct REPARSE_KEY rkey; ++ struct NTFS_DE_R *re; ++ struct INDEX_ROOT *root_r; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ rkey.ReparseTag = rtag; ++ rkey.ref = *ref; ++ ++ mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE); ++ ++ if (rtag) { ++ err = indx_delete_entry(indx, ni, &rkey, sizeof(rkey), NULL); ++ goto out1; ++ } ++ ++ fnd = fnd_get(); ++ if (!fnd) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ root_r = indx_get_root(indx, ni, NULL, NULL); ++ if (!root_r) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* 1 - forces to ignore rkey.ReparseTag when comparing keys */ ++ err = indx_find(indx, ni, root_r, &rkey, sizeof(rkey), (void *)1, &diff, ++ (struct NTFS_DE **)&re, fnd); ++ if (err) ++ goto out; ++ ++ if (memcmp(&re->key.ref, ref, sizeof(*ref))) { ++ /* Impossible. Looks like volume corrupt?*/ ++ goto out; ++ } ++ ++ memcpy(&rkey, &re->key, sizeof(rkey)); ++ ++ fnd_put(fnd); ++ fnd = NULL; ++ ++ err = indx_delete_entry(indx, ni, &rkey, sizeof(rkey), NULL); ++ if (err) ++ goto out; ++ ++out: ++ fnd_put(fnd); ++ ++out1: ++ mark_inode_dirty(&ni->vfs_inode); ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++static inline void ntfs_unmap_and_discard(struct ntfs_sb_info *sbi, CLST lcn, ++ CLST len) ++{ ++ ntfs_unmap_meta(sbi->sb, lcn, len); ++ ntfs_discard(sbi, lcn, len); ++} ++ ++void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim) ++{ ++ CLST end, i; ++ struct wnd_bitmap *wnd = &sbi->used.bitmap; ++ ++ down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); ++ if (!wnd_is_used(wnd, lcn, len)) { ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ ++ end = lcn + len; ++ len = 0; ++ for (i = lcn; i < end; i++) { ++ if (wnd_is_used(wnd, i, 1)) { ++ if (!len) ++ lcn = i; ++ len += 1; ++ continue; ++ } ++ ++ if (!len) ++ continue; ++ ++ if (trim) ++ ntfs_unmap_and_discard(sbi, lcn, len); ++ ++ wnd_set_free(wnd, lcn, len); ++ len = 0; ++ } ++ ++ if (!len) ++ goto out; ++ } ++ ++ if (trim) ++ ntfs_unmap_and_discard(sbi, lcn, len); ++ wnd_set_free(wnd, lcn, len); ++ ++out: ++ up_write(&wnd->rw_lock); ++} ++ ++/* ++ * run_deallocate ++ * ++ * deallocate clusters ++ */ ++int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim) ++{ ++ CLST lcn, len; ++ size_t idx = 0; ++ ++ while (run_get_entry(run, idx++, NULL, &lcn, &len)) { ++ if (lcn == SPARSE_LCN) ++ continue; ++ ++ mark_as_free_ex(sbi, lcn, len, trim); ++ } ++ ++ return 0; ++} +diff --git a/src/index.c b/src/index.c +new file mode 100644 +index 0000000000000000000000000000000000000000..6aa9540ece4728b5a02108924db76b4ee920a353 +--- /dev/null ++++ b/src/index.c +@@ -0,0 +1,2647 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++static const struct INDEX_NAMES { ++ const __le16 *name; ++ u8 name_len; ++} s_index_names[INDEX_MUTEX_TOTAL] = { ++ { I30_NAME, ARRAY_SIZE(I30_NAME) }, { SII_NAME, ARRAY_SIZE(SII_NAME) }, ++ { SDH_NAME, ARRAY_SIZE(SDH_NAME) }, { SO_NAME, ARRAY_SIZE(SO_NAME) }, ++ { SQ_NAME, ARRAY_SIZE(SQ_NAME) }, { SR_NAME, ARRAY_SIZE(SR_NAME) }, ++}; ++ ++/* ++ * compare two names in index ++ * if l1 != 0 ++ * both names are little endian on-disk ATTR_FILE_NAME structs ++ * else ++ * key1 - cpu_str, key2 - ATTR_FILE_NAME ++ */ ++static int cmp_fnames(const void *key1, size_t l1, const void *key2, size_t l2, ++ const void *data) ++{ ++ const struct ATTR_FILE_NAME *f2 = key2; ++ const struct ntfs_sb_info *sbi = data; ++ const struct ATTR_FILE_NAME *f1; ++ u16 fsize2; ++ bool both_case; ++ ++ if (l2 <= offsetof(struct ATTR_FILE_NAME, name)) ++ return -1; ++ ++ fsize2 = fname_full_size(f2); ++ if (l2 < fsize2) ++ return -1; ++ ++ both_case = f2->type != FILE_NAME_DOS /*&& !sbi->options.nocase*/; ++ if (!l1) { ++ const struct le_str *s2 = (struct le_str *)&f2->name_len; ++ ++ /* ++ * If names are equal (case insensitive) ++ * try to compare it case sensitive ++ */ ++ return ntfs_cmp_names_cpu(key1, s2, sbi->upcase, both_case); ++ } ++ ++ f1 = key1; ++ return ntfs_cmp_names(f1->name, f1->name_len, f2->name, f2->name_len, ++ sbi->upcase, both_case); ++} ++ ++/* $SII of $Secure and $Q of Quota */ ++static int cmp_uint(const void *key1, size_t l1, const void *key2, size_t l2, ++ const void *data) ++{ ++ const u32 *k1 = key1; ++ const u32 *k2 = key2; ++ ++ if (l2 < sizeof(u32)) ++ return -1; ++ ++ if (*k1 < *k2) ++ return -1; ++ if (*k1 > *k2) ++ return 1; ++ return 0; ++} ++ ++/* $SDH of $Secure */ ++static int cmp_sdh(const void *key1, size_t l1, const void *key2, size_t l2, ++ const void *data) ++{ ++ const struct SECURITY_KEY *k1 = key1; ++ const struct SECURITY_KEY *k2 = key2; ++ u32 t1, t2; ++ ++ if (l2 < sizeof(struct SECURITY_KEY)) ++ return -1; ++ ++ t1 = le32_to_cpu(k1->hash); ++ t2 = le32_to_cpu(k2->hash); ++ ++ /* First value is a hash value itself */ ++ if (t1 < t2) ++ return -1; ++ if (t1 > t2) ++ return 1; ++ ++ /* Second value is security Id */ ++ if (data) { ++ t1 = le32_to_cpu(k1->sec_id); ++ t2 = le32_to_cpu(k2->sec_id); ++ if (t1 < t2) ++ return -1; ++ if (t1 > t2) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* $O of ObjId and "$R" for Reparse */ ++static int cmp_uints(const void *key1, size_t l1, const void *key2, size_t l2, ++ const void *data) ++{ ++ const __le32 *k1 = key1; ++ const __le32 *k2 = key2; ++ size_t count; ++ ++ if ((size_t)data == 1) { ++ /* ++ * ni_delete_all -> ntfs_remove_reparse -> delete all with this reference ++ * k1, k2 - pointers to REPARSE_KEY ++ */ ++ ++ k1 += 1; // skip REPARSE_KEY.ReparseTag ++ k2 += 1; // skip REPARSE_KEY.ReparseTag ++ if (l2 <= sizeof(int)) ++ return -1; ++ l2 -= sizeof(int); ++ if (l1 <= sizeof(int)) ++ return 1; ++ l1 -= sizeof(int); ++ } ++ ++ if (l2 < sizeof(int)) ++ return -1; ++ ++ for (count = min(l1, l2) >> 2; count > 0; --count, ++k1, ++k2) { ++ u32 t1 = le32_to_cpu(*k1); ++ u32 t2 = le32_to_cpu(*k2); ++ ++ if (t1 > t2) ++ return 1; ++ if (t1 < t2) ++ return -1; ++ } ++ ++ if (l1 > l2) ++ return 1; ++ if (l1 < l2) ++ return -1; ++ ++ return 0; ++} ++ ++static inline NTFS_CMP_FUNC get_cmp_func(const struct INDEX_ROOT *root) ++{ ++ switch (root->type) { ++ case ATTR_NAME: ++ if (root->rule == NTFS_COLLATION_TYPE_FILENAME) ++ return &cmp_fnames; ++ break; ++ case ATTR_ZERO: ++ switch (root->rule) { ++ case NTFS_COLLATION_TYPE_UINT: ++ return &cmp_uint; ++ case NTFS_COLLATION_TYPE_SECURITY_HASH: ++ return &cmp_sdh; ++ case NTFS_COLLATION_TYPE_UINTS: ++ return &cmp_uints; ++ default: ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return NULL; ++} ++ ++struct bmp_buf { ++ struct ATTRIB *b; ++ struct mft_inode *mi; ++ struct buffer_head *bh; ++ ulong *buf; ++ size_t bit; ++ u32 nbits; ++ u64 new_valid; ++}; ++ ++static int bmp_buf_get(struct ntfs_index *indx, struct ntfs_inode *ni, ++ size_t bit, struct bmp_buf *bbuf) ++{ ++ struct ATTRIB *b; ++ size_t data_size, valid_size, vbo, off = bit >> 3; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ CLST vcn = off >> sbi->cluster_bits; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct buffer_head *bh; ++ struct super_block *sb; ++ u32 blocksize; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ ++ bbuf->bh = NULL; ++ ++ b = ni_find_attr(ni, NULL, &le, ATTR_BITMAP, in->name, in->name_len, ++ &vcn, &bbuf->mi); ++ bbuf->b = b; ++ if (!b) ++ return -EINVAL; ++ ++ if (!b->non_res) { ++ data_size = le32_to_cpu(b->res.data_size); ++ ++ if (off >= data_size) ++ return -EINVAL; ++ ++ bbuf->buf = (ulong *)resident_data(b); ++ bbuf->bit = 0; ++ bbuf->nbits = data_size * 8; ++ ++ return 0; ++ } ++ ++ data_size = le64_to_cpu(b->nres.data_size); ++ if (WARN_ON(off >= data_size)) { ++ /* looks like filesystem error */ ++ return -EINVAL; ++ } ++ ++ valid_size = le64_to_cpu(b->nres.valid_size); ++ ++ bh = ntfs_bread_run(sbi, &indx->bitmap_run, off); ++ if (!bh) ++ return -EIO; ++ ++ if (IS_ERR(bh)) ++ return PTR_ERR(bh); ++ ++ bbuf->bh = bh; ++ ++ if (buffer_locked(bh)) ++ __wait_on_buffer(bh); ++ ++ lock_buffer(bh); ++ ++ sb = sbi->sb; ++ blocksize = sb->s_blocksize; ++ ++ vbo = off & ~(size_t)sbi->block_mask; ++ ++ bbuf->new_valid = vbo + blocksize; ++ if (bbuf->new_valid <= valid_size) ++ bbuf->new_valid = 0; ++ else if (bbuf->new_valid > data_size) ++ bbuf->new_valid = data_size; ++ ++ if (vbo >= valid_size) { ++ memset(bh->b_data, 0, blocksize); ++ } else if (vbo + blocksize > valid_size) { ++ u32 voff = valid_size & sbi->block_mask; ++ ++ memset(bh->b_data + voff, 0, blocksize - voff); ++ } ++ ++ bbuf->buf = (ulong *)bh->b_data; ++ bbuf->bit = 8 * (off & ~(size_t)sbi->block_mask); ++ bbuf->nbits = 8 * blocksize; ++ ++ return 0; ++} ++ ++static void bmp_buf_put(struct bmp_buf *bbuf, bool dirty) ++{ ++ struct buffer_head *bh = bbuf->bh; ++ struct ATTRIB *b = bbuf->b; ++ ++ if (!bh) { ++ if (b && !b->non_res && dirty) ++ bbuf->mi->dirty = true; ++ return; ++ } ++ ++ if (!dirty) ++ goto out; ++ ++ if (bbuf->new_valid) { ++ b->nres.valid_size = cpu_to_le64(bbuf->new_valid); ++ bbuf->mi->dirty = true; ++ } ++ ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ ++out: ++ unlock_buffer(bh); ++ put_bh(bh); ++} ++ ++/* ++ * indx_mark_used ++ * ++ * marks the bit 'bit' as used ++ */ ++static int indx_mark_used(struct ntfs_index *indx, struct ntfs_inode *ni, ++ size_t bit) ++{ ++ int err; ++ struct bmp_buf bbuf; ++ ++ err = bmp_buf_get(indx, ni, bit, &bbuf); ++ if (err) ++ return err; ++ ++ __set_bit(bit - bbuf.bit, bbuf.buf); ++ ++ bmp_buf_put(&bbuf, true); ++ ++ return 0; ++} ++ ++/* ++ * indx_mark_free ++ * ++ * the bit 'bit' as free ++ */ ++static int indx_mark_free(struct ntfs_index *indx, struct ntfs_inode *ni, ++ size_t bit) ++{ ++ int err; ++ struct bmp_buf bbuf; ++ ++ err = bmp_buf_get(indx, ni, bit, &bbuf); ++ if (err) ++ return err; ++ ++ __clear_bit(bit - bbuf.bit, bbuf.buf); ++ ++ bmp_buf_put(&bbuf, true); ++ ++ return 0; ++} ++ ++/* ++ * if ntfs_readdir calls this function (indx_used_bit -> scan_nres_bitmap), ++ * inode is shared locked and no ni_lock ++ * use rw_semaphore for read/write access to bitmap_run ++ */ ++static int scan_nres_bitmap(struct ntfs_inode *ni, struct ATTRIB *bitmap, ++ struct ntfs_index *indx, size_t from, ++ bool (*fn)(const ulong *buf, u32 bit, u32 bits, ++ size_t *ret), ++ size_t *ret) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct super_block *sb = sbi->sb; ++ struct runs_tree *run = &indx->bitmap_run; ++ struct rw_semaphore *lock = &indx->run_lock; ++ u32 nbits = sb->s_blocksize * 8; ++ u32 blocksize = sb->s_blocksize; ++ u64 valid_size = le64_to_cpu(bitmap->nres.valid_size); ++ u64 data_size = le64_to_cpu(bitmap->nres.data_size); ++ sector_t eblock = bytes_to_block(sb, data_size); ++ size_t vbo = from >> 3; ++ sector_t blk = (vbo & sbi->cluster_mask) >> sb->s_blocksize_bits; ++ sector_t vblock = vbo >> sb->s_blocksize_bits; ++ sector_t blen, block; ++ CLST lcn, clen, vcn, vcn_next; ++ size_t idx; ++ struct buffer_head *bh; ++ bool ok; ++ ++ *ret = MINUS_ONE_T; ++ ++ if (vblock >= eblock) ++ return 0; ++ ++ from &= nbits - 1; ++ vcn = vbo >> sbi->cluster_bits; ++ ++ down_read(lock); ++ ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); ++ up_read(lock); ++ ++next_run: ++ if (!ok) { ++ int err; ++ const struct INDEX_NAMES *name = &s_index_names[indx->type]; ++ ++ down_write(lock); ++ err = attr_load_runs_vcn(ni, ATTR_BITMAP, name->name, ++ name->name_len, run, vcn); ++ up_write(lock); ++ if (err) ++ return err; ++ down_read(lock); ++ ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); ++ up_read(lock); ++ if (!ok) ++ return -EINVAL; ++ } ++ ++ blen = (sector_t)clen * sbi->blocks_per_cluster; ++ block = (sector_t)lcn * sbi->blocks_per_cluster; ++ ++ for (; blk < blen; blk++, from = 0) { ++ bh = ntfs_bread(sb, block + blk); ++ if (!bh) ++ return -EIO; ++ ++ vbo = (u64)vblock << sb->s_blocksize_bits; ++ if (vbo >= valid_size) { ++ memset(bh->b_data, 0, blocksize); ++ } else if (vbo + blocksize > valid_size) { ++ u32 voff = valid_size & sbi->block_mask; ++ ++ memset(bh->b_data + voff, 0, blocksize - voff); ++ } ++ ++ if (vbo + blocksize > data_size) ++ nbits = 8 * (data_size - vbo); ++ ++ ok = nbits > from ? (*fn)((ulong *)bh->b_data, from, nbits, ret) ++ : false; ++ put_bh(bh); ++ ++ if (ok) { ++ *ret += 8 * vbo; ++ return 0; ++ } ++ ++ if (++vblock >= eblock) { ++ *ret = MINUS_ONE_T; ++ return 0; ++ } ++ } ++ blk = 0; ++ vcn_next = vcn + clen; ++ down_read(lock); ++ ok = run_get_entry(run, ++idx, &vcn, &lcn, &clen) && vcn == vcn_next; ++ if (!ok) ++ vcn = vcn_next; ++ up_read(lock); ++ goto next_run; ++} ++ ++static bool scan_for_free(const ulong *buf, u32 bit, u32 bits, size_t *ret) ++{ ++ size_t pos = find_next_zero_bit(buf, bits, bit); ++ ++ if (pos >= bits) ++ return false; ++ *ret = pos; ++ return true; ++} ++ ++/* ++ * indx_find_free ++ * ++ * looks for free bit ++ * returns -1 if no free bits ++ */ ++static int indx_find_free(struct ntfs_index *indx, struct ntfs_inode *ni, ++ size_t *bit, struct ATTRIB **bitmap) ++{ ++ struct ATTRIB *b; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ int err; ++ ++ b = ni_find_attr(ni, NULL, &le, ATTR_BITMAP, in->name, in->name_len, ++ NULL, NULL); ++ ++ if (!b) ++ return -ENOENT; ++ ++ *bitmap = b; ++ *bit = MINUS_ONE_T; ++ ++ if (!b->non_res) { ++ u32 nbits = 8 * le32_to_cpu(b->res.data_size); ++ size_t pos = find_next_zero_bit(resident_data(b), nbits, 0); ++ ++ if (pos < nbits) ++ *bit = pos; ++ } else { ++ err = scan_nres_bitmap(ni, b, indx, 0, &scan_for_free, bit); ++ ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static bool scan_for_used(const ulong *buf, u32 bit, u32 bits, size_t *ret) ++{ ++ size_t pos = find_next_bit(buf, bits, bit); ++ ++ if (pos >= bits) ++ return false; ++ *ret = pos; ++ return true; ++} ++ ++/* ++ * indx_used_bit ++ * ++ * looks for used bit ++ * returns MINUS_ONE_T if no used bits ++ */ ++int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit) ++{ ++ struct ATTRIB *b; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ size_t from = *bit; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ int err; ++ ++ b = ni_find_attr(ni, NULL, &le, ATTR_BITMAP, in->name, in->name_len, ++ NULL, NULL); ++ ++ if (!b) ++ return -ENOENT; ++ ++ *bit = MINUS_ONE_T; ++ ++ if (!b->non_res) { ++ u32 nbits = le32_to_cpu(b->res.data_size) * 8; ++ size_t pos = find_next_bit(resident_data(b), nbits, from); ++ ++ if (pos < nbits) ++ *bit = pos; ++ } else { ++ err = scan_nres_bitmap(ni, b, indx, from, &scan_for_used, bit); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* ++ * hdr_find_split ++ * ++ * finds a point at which the index allocation buffer would like to ++ * be split. ++ * NOTE: This function should never return 'END' entry NULL returns on error ++ */ ++static const struct NTFS_DE *hdr_find_split(const struct INDEX_HDR *hdr) ++{ ++ size_t o; ++ const struct NTFS_DE *e = hdr_first_de(hdr); ++ u32 used_2 = le32_to_cpu(hdr->used) >> 1; ++ u16 esize = le16_to_cpu(e->size); ++ ++ if (!e || de_is_last(e)) ++ return NULL; ++ ++ for (o = le32_to_cpu(hdr->de_off) + esize; o < used_2; o += esize) { ++ const struct NTFS_DE *p = e; ++ ++ e = Add2Ptr(hdr, o); ++ ++ /* We must not return END entry */ ++ if (de_is_last(e)) ++ return p; ++ ++ esize = le16_to_cpu(e->size); ++ } ++ ++ return e; ++} ++ ++/* ++ * hdr_insert_head ++ * ++ * inserts some entries at the beginning of the buffer. ++ * It is used to insert entries into a newly-created buffer. ++ */ ++static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr, ++ const void *ins, u32 ins_bytes) ++{ ++ u32 to_move; ++ struct NTFS_DE *e = hdr_first_de(hdr); ++ u32 used = le32_to_cpu(hdr->used); ++ ++ if (!e) ++ return NULL; ++ ++ /* Now we just make room for the inserted entries and jam it in. */ ++ to_move = used - le32_to_cpu(hdr->de_off); ++ memmove(Add2Ptr(e, ins_bytes), e, to_move); ++ memcpy(e, ins, ins_bytes); ++ hdr->used = cpu_to_le32(used + ins_bytes); ++ ++ return e; ++} ++ ++void fnd_clear(struct ntfs_fnd *fnd) ++{ ++ int i; ++ ++ for (i = 0; i < fnd->level; i++) { ++ struct indx_node *n = fnd->nodes[i]; ++ ++ if (!n) ++ continue; ++ ++ put_indx_node(n); ++ fnd->nodes[i] = NULL; ++ } ++ fnd->level = 0; ++ fnd->root_de = NULL; ++} ++ ++static int fnd_push(struct ntfs_fnd *fnd, struct indx_node *n, ++ struct NTFS_DE *e) ++{ ++ int i; ++ ++ i = fnd->level; ++ if (i < 0 || i >= ARRAY_SIZE(fnd->nodes)) ++ return -EINVAL; ++ fnd->nodes[i] = n; ++ fnd->de[i] = e; ++ fnd->level += 1; ++ return 0; ++} ++ ++static struct indx_node *fnd_pop(struct ntfs_fnd *fnd) ++{ ++ struct indx_node *n; ++ int i = fnd->level; ++ ++ i -= 1; ++ n = fnd->nodes[i]; ++ fnd->nodes[i] = NULL; ++ fnd->level = i; ++ ++ return n; ++} ++ ++static bool fnd_is_empty(struct ntfs_fnd *fnd) ++{ ++ if (!fnd->level) ++ return !fnd->root_de; ++ ++ return !fnd->de[fnd->level - 1]; ++} ++ ++/* ++ * hdr_find_e ++ * ++ * locates an entry the index buffer. ++ * If no matching entry is found, it returns the first entry which is greater ++ * than the desired entry If the search key is greater than all the entries the ++ * buffer, it returns the 'end' entry. This function does a binary search of the ++ * current index buffer, for the first entry that is <= to the search value ++ * Returns NULL if error ++ */ ++static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, ++ const struct INDEX_HDR *hdr, const void *key, ++ size_t key_len, const void *ctx, int *diff) ++{ ++ struct NTFS_DE *e; ++ NTFS_CMP_FUNC cmp = indx->cmp; ++ u32 e_size, e_key_len; ++ u32 end = le32_to_cpu(hdr->used); ++ u32 off = le32_to_cpu(hdr->de_off); ++ ++#ifdef NTFS3_INDEX_BINARY_SEARCH ++ int max_idx = 0, fnd, min_idx; ++ int nslots = 64; ++ u16 *offs; ++ ++ if (end > 0x10000) ++ goto next; ++ ++ offs = ntfs_malloc(sizeof(u16) * nslots); ++ if (!offs) ++ goto next; ++ ++ /* use binary search algorithm */ ++next1: ++ if (off + sizeof(struct NTFS_DE) > end) { ++ e = NULL; ++ goto out1; ++ } ++ e = Add2Ptr(hdr, off); ++ e_size = le16_to_cpu(e->size); ++ ++ if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) { ++ e = NULL; ++ goto out1; ++ } ++ ++ if (max_idx >= nslots) { ++ u16 *ptr; ++ int new_slots = QuadAlign(2 * nslots); ++ ++ ptr = ntfs_malloc(sizeof(u16) * new_slots); ++ if (ptr) ++ memcpy(ptr, offs, sizeof(u16) * max_idx); ++ ntfs_free(offs); ++ offs = ptr; ++ nslots = new_slots; ++ if (!ptr) ++ goto next; ++ } ++ ++ /* Store entry table */ ++ offs[max_idx] = off; ++ ++ if (!de_is_last(e)) { ++ off += e_size; ++ max_idx += 1; ++ goto next1; ++ } ++ ++ /* ++ * Table of pointers is created ++ * Use binary search to find entry that is <= to the search value ++ */ ++ fnd = -1; ++ min_idx = 0; ++ ++ while (min_idx <= max_idx) { ++ int mid_idx = min_idx + ((max_idx - min_idx) >> 1); ++ int diff2; ++ ++ e = Add2Ptr(hdr, offs[mid_idx]); ++ ++ e_key_len = le16_to_cpu(e->key_size); ++ ++ diff2 = (*cmp)(key, key_len, e + 1, e_key_len, ctx); ++ ++ if (!diff2) { ++ *diff = 0; ++ goto out1; ++ } ++ ++ if (diff2 < 0) { ++ max_idx = mid_idx - 1; ++ fnd = mid_idx; ++ if (!fnd) ++ break; ++ } else { ++ min_idx = mid_idx + 1; ++ } ++ } ++ ++ if (fnd == -1) { ++ e = NULL; ++ goto out1; ++ } ++ ++ *diff = -1; ++ e = Add2Ptr(hdr, offs[fnd]); ++ ++out1: ++ ntfs_free(offs); ++ ++ return e; ++#endif ++ ++next: ++ /* ++ * Entries index are sorted ++ * Enumerate all entries until we find entry that is <= to the search value ++ */ ++ if (off + sizeof(struct NTFS_DE) > end) ++ return NULL; ++ ++ e = Add2Ptr(hdr, off); ++ e_size = le16_to_cpu(e->size); ++ ++ if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) ++ return NULL; ++ ++ off += e_size; ++ ++ e_key_len = le16_to_cpu(e->key_size); ++ ++ *diff = (*cmp)(key, key_len, e + 1, e_key_len, ctx); ++ if (!*diff) ++ return e; ++ ++ if (*diff <= 0) ++ return e; ++ ++ if (de_is_last(e)) { ++ *diff = 1; ++ return e; ++ } ++ goto next; ++} ++ ++/* ++ * hdr_insert_de ++ * ++ * inserts an index entry into the buffer. ++ * 'before' should be a pointer previously returned from hdr_find_e ++ */ ++static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, ++ struct INDEX_HDR *hdr, ++ const struct NTFS_DE *de, ++ struct NTFS_DE *before, const void *ctx) ++{ ++ int diff; ++ size_t off = PtrOffset(hdr, before); ++ u32 used = le32_to_cpu(hdr->used); ++ u32 total = le32_to_cpu(hdr->total); ++ u16 de_size = le16_to_cpu(de->size); ++ ++ /* First, check to see if there's enough room */ ++ if (used + de_size > total) ++ return NULL; ++ ++ /* We know there's enough space, so we know we'll succeed. */ ++ if (before) { ++ /* Check that before is inside Index */ ++ if (off >= used || off < le32_to_cpu(hdr->de_off) || ++ off + le16_to_cpu(before->size) > total) { ++ return NULL; ++ } ++ goto ok; ++ } ++ /* No insert point is applied. Get it manually */ ++ before = hdr_find_e(indx, hdr, de + 1, le16_to_cpu(de->key_size), ctx, ++ &diff); ++ if (!before) ++ return NULL; ++ off = PtrOffset(hdr, before); ++ ++ok: ++ /* Now we just make room for the entry and jam it in. */ ++ memmove(Add2Ptr(before, de_size), before, used - off); ++ ++ hdr->used = cpu_to_le32(used + de_size); ++ memcpy(before, de, de_size); ++ ++ return before; ++} ++ ++/* ++ * hdr_delete_de ++ * ++ * removes an entry from the index buffer ++ */ ++static inline struct NTFS_DE *hdr_delete_de(struct INDEX_HDR *hdr, ++ struct NTFS_DE *re) ++{ ++ u32 used = le32_to_cpu(hdr->used); ++ u16 esize = le16_to_cpu(re->size); ++ u32 off = PtrOffset(hdr, re); ++ int bytes = used - (off + esize); ++ ++ if (off >= used || esize < sizeof(struct NTFS_DE) || ++ bytes < sizeof(struct NTFS_DE)) ++ return NULL; ++ ++ hdr->used = cpu_to_le32(used - esize); ++ memmove(re, Add2Ptr(re, esize), bytes); ++ ++ return re; ++} ++ ++void indx_clear(struct ntfs_index *indx) ++{ ++ run_close(&indx->alloc_run); ++ run_close(&indx->bitmap_run); ++} ++ ++int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, ++ const struct ATTRIB *attr, enum index_mutex_classed type) ++{ ++ u32 t32; ++ const struct INDEX_ROOT *root = resident_data(attr); ++ ++ /* Check root fields */ ++ if (!root->index_block_clst) ++ return -EINVAL; ++ ++ indx->type = type; ++ indx->idx2vbn_bits = __ffs(root->index_block_clst); ++ ++ t32 = le32_to_cpu(root->index_block_size); ++ indx->index_bits = blksize_bits(t32); ++ ++ /* Check index record size */ ++ if (t32 < sbi->cluster_size) { ++ /* index record is smaller than a cluster, use 512 blocks */ ++ if (t32 != root->index_block_clst * SECTOR_SIZE) ++ return -EINVAL; ++ ++ /* Check alignment to a cluster */ ++ if ((sbi->cluster_size >> SECTOR_SHIFT) & ++ (root->index_block_clst - 1)) { ++ return -EINVAL; ++ } ++ ++ indx->vbn2vbo_bits = SECTOR_SHIFT; ++ } else { ++ /* index record must be a multiple of cluster size */ ++ if (t32 != root->index_block_clst << sbi->cluster_bits) ++ return -EINVAL; ++ ++ indx->vbn2vbo_bits = sbi->cluster_bits; ++ } ++ ++ init_rwsem(&indx->run_lock); ++ ++ indx->cmp = get_cmp_func(root); ++ return indx->cmp ? 0 : -EINVAL; ++} ++ ++static struct indx_node *indx_new(struct ntfs_index *indx, ++ struct ntfs_inode *ni, CLST vbn, ++ const __le64 *sub_vbn) ++{ ++ int err; ++ struct NTFS_DE *e; ++ struct indx_node *r; ++ struct INDEX_HDR *hdr; ++ struct INDEX_BUFFER *index; ++ u64 vbo = (u64)vbn << indx->vbn2vbo_bits; ++ u32 bytes = 1u << indx->index_bits; ++ u16 fn; ++ u32 eo; ++ ++ r = ntfs_zalloc(sizeof(struct indx_node)); ++ if (!r) ++ return ERR_PTR(-ENOMEM); ++ ++ index = ntfs_zalloc(bytes); ++ if (!index) { ++ ntfs_free(r); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ err = ntfs_get_bh(ni->mi.sbi, &indx->alloc_run, vbo, bytes, &r->nb); ++ ++ if (err) { ++ ntfs_free(index); ++ ntfs_free(r); ++ return ERR_PTR(err); ++ } ++ ++ /* Create header */ ++ index->rhdr.sign = NTFS_INDX_SIGNATURE; ++ index->rhdr.fix_off = cpu_to_le16(sizeof(struct INDEX_BUFFER)); // 0x28 ++ fn = (bytes >> SECTOR_SHIFT) + 1; // 9 ++ index->rhdr.fix_num = cpu_to_le16(fn); ++ index->vbn = cpu_to_le64(vbn); ++ hdr = &index->ihdr; ++ eo = QuadAlign(sizeof(struct INDEX_BUFFER) + fn * sizeof(short)); ++ hdr->de_off = cpu_to_le32(eo); ++ ++ e = Add2Ptr(hdr, eo); ++ ++ if (sub_vbn) { ++ e->flags = NTFS_IE_LAST | NTFS_IE_HAS_SUBNODES; ++ e->size = cpu_to_le16(sizeof(struct NTFS_DE) + sizeof(u64)); ++ hdr->used = ++ cpu_to_le32(eo + sizeof(struct NTFS_DE) + sizeof(u64)); ++ de_set_vbn_le(e, *sub_vbn); ++ hdr->flags = 1; ++ } else { ++ e->size = cpu_to_le16(sizeof(struct NTFS_DE)); ++ hdr->used = cpu_to_le32(eo + sizeof(struct NTFS_DE)); ++ e->flags = NTFS_IE_LAST; ++ } ++ ++ hdr->total = cpu_to_le32(bytes - offsetof(struct INDEX_BUFFER, ihdr)); ++ ++ r->index = index; ++ return r; ++} ++ ++struct INDEX_ROOT *indx_get_root(struct ntfs_index *indx, struct ntfs_inode *ni, ++ struct ATTRIB **attr, struct mft_inode **mi) ++{ ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct ATTRIB *a; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ ++ a = ni_find_attr(ni, NULL, &le, ATTR_ROOT, in->name, in->name_len, NULL, ++ mi); ++ if (!a) ++ return NULL; ++ ++ if (attr) ++ *attr = a; ++ ++ return resident_data_ex(a, sizeof(struct INDEX_ROOT)); ++} ++ ++static int indx_write(struct ntfs_index *indx, struct ntfs_inode *ni, ++ struct indx_node *node, int sync) ++{ ++ struct INDEX_BUFFER *ib = node->index; ++ ++ return ntfs_write_bh(ni->mi.sbi, &ib->rhdr, &node->nb, sync); ++} ++ ++/* ++ * if ntfs_readdir calls this function ++ * inode is shared locked and no ni_lock ++ * use rw_semaphore for read/write access to alloc_run ++ */ ++int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn, ++ struct indx_node **node) ++{ ++ int err; ++ struct INDEX_BUFFER *ib; ++ struct runs_tree *run = &indx->alloc_run; ++ struct rw_semaphore *lock = &indx->run_lock; ++ u64 vbo = (u64)vbn << indx->vbn2vbo_bits; ++ u32 bytes = 1u << indx->index_bits; ++ struct indx_node *in = *node; ++ const struct INDEX_NAMES *name; ++ ++ if (!in) { ++ in = ntfs_zalloc(sizeof(struct indx_node)); ++ if (!in) ++ return -ENOMEM; ++ } else { ++ nb_put(&in->nb); ++ } ++ ++ ib = in->index; ++ if (!ib) { ++ ib = ntfs_malloc(bytes); ++ if (!ib) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ down_read(lock); ++ err = ntfs_read_bh(ni->mi.sbi, run, vbo, &ib->rhdr, bytes, &in->nb); ++ up_read(lock); ++ if (!err) ++ goto ok; ++ ++ if (err == -E_NTFS_FIXUP) ++ goto ok; ++ ++ if (err != -ENOENT) ++ goto out; ++ ++ name = &s_index_names[indx->type]; ++ down_write(lock); ++ err = attr_load_runs_range(ni, ATTR_ALLOC, name->name, name->name_len, ++ run, vbo, vbo + bytes); ++ up_write(lock); ++ if (err) ++ goto out; ++ ++ down_read(lock); ++ err = ntfs_read_bh(ni->mi.sbi, run, vbo, &ib->rhdr, bytes, &in->nb); ++ up_read(lock); ++ if (err == -E_NTFS_FIXUP) ++ goto ok; ++ ++ if (err) ++ goto out; ++ ++ok: ++ if (err == -E_NTFS_FIXUP) { ++ ntfs_write_bh(ni->mi.sbi, &ib->rhdr, &in->nb, 0); ++ err = 0; ++ } ++ ++ in->index = ib; ++ *node = in; ++ ++out: ++ if (ib != in->index) ++ ntfs_free(ib); ++ ++ if (*node != in) { ++ nb_put(&in->nb); ++ ntfs_free(in); ++ } ++ ++ return err; ++} ++ ++/* ++ * indx_find ++ * ++ * scans NTFS directory for given entry ++ */ ++int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct INDEX_ROOT *root, const void *key, size_t key_len, ++ const void *ctx, int *diff, struct NTFS_DE **entry, ++ struct ntfs_fnd *fnd) ++{ ++ int err; ++ struct NTFS_DE *e; ++ const struct INDEX_HDR *hdr; ++ struct indx_node *node; ++ ++ if (!root) ++ root = indx_get_root(&ni->dir, ni, NULL, NULL); ++ ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ hdr = &root->ihdr; ++ ++ /* Check cache */ ++ e = fnd->level ? fnd->de[fnd->level - 1] : fnd->root_de; ++ if (e && !de_is_last(e) && ++ !(*indx->cmp)(key, key_len, e + 1, le16_to_cpu(e->key_size), ctx)) { ++ *entry = e; ++ *diff = 0; ++ return 0; ++ } ++ ++ /* Soft finder reset */ ++ fnd_clear(fnd); ++ ++ /* Lookup entry that is <= to the search value */ ++ e = hdr_find_e(indx, hdr, key, key_len, ctx, diff); ++ if (!e) ++ return -EINVAL; ++ ++ if (fnd) ++ fnd->root_de = e; ++ ++ err = 0; ++ ++ for (;;) { ++ node = NULL; ++ if (*diff >= 0 || !de_has_vcn_ex(e)) { ++ *entry = e; ++ goto out; ++ } ++ ++ /* Read next level. */ ++ err = indx_read(indx, ni, de_get_vbn(e), &node); ++ if (err) ++ goto out; ++ ++ /* Lookup entry that is <= to the search value */ ++ e = hdr_find_e(indx, &node->index->ihdr, key, key_len, ctx, ++ diff); ++ if (!e) { ++ err = -EINVAL; ++ put_indx_node(node); ++ goto out; ++ } ++ ++ fnd_push(fnd, node, e); ++ } ++ ++out: ++ return err; ++} ++ ++int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct INDEX_ROOT *root, struct NTFS_DE **entry, ++ struct ntfs_fnd *fnd) ++{ ++ int err; ++ struct indx_node *n = NULL; ++ struct NTFS_DE *e; ++ size_t iter = 0; ++ int level = fnd->level; ++ ++ if (!*entry) { ++ /* Start find */ ++ e = hdr_first_de(&root->ihdr); ++ if (!e) ++ return 0; ++ fnd_clear(fnd); ++ fnd->root_de = e; ++ } else if (!level) { ++ if (de_is_last(fnd->root_de)) { ++ *entry = NULL; ++ return 0; ++ } ++ ++ e = hdr_next_de(&root->ihdr, fnd->root_de); ++ if (!e) ++ return -EINVAL; ++ fnd->root_de = e; ++ } else { ++ n = fnd->nodes[level - 1]; ++ e = fnd->de[level - 1]; ++ ++ if (de_is_last(e)) ++ goto pop_level; ++ ++ e = hdr_next_de(&n->index->ihdr, e); ++ if (!e) ++ return -EINVAL; ++ ++ fnd->de[level - 1] = e; ++ } ++ ++ /* Just to avoid tree cycle */ ++next_iter: ++ if (iter++ >= 1000) ++ return -EINVAL; ++ ++ while (de_has_vcn_ex(e)) { ++ if (le16_to_cpu(e->size) < ++ sizeof(struct NTFS_DE) + sizeof(u64)) { ++ if (n) { ++ fnd_pop(fnd); ++ ntfs_free(n); ++ } ++ return -EINVAL; ++ } ++ ++ /* Read next level */ ++ err = indx_read(indx, ni, de_get_vbn(e), &n); ++ if (err) ++ return err; ++ ++ /* Try next level */ ++ e = hdr_first_de(&n->index->ihdr); ++ if (!e) { ++ ntfs_free(n); ++ return -EINVAL; ++ } ++ ++ fnd_push(fnd, n, e); ++ } ++ ++ if (le16_to_cpu(e->size) > sizeof(struct NTFS_DE)) { ++ *entry = e; ++ return 0; ++ } ++ ++pop_level: ++ for (;;) { ++ if (!de_is_last(e)) ++ goto next_iter; ++ ++ /* Pop one level */ ++ if (n) { ++ fnd_pop(fnd); ++ ntfs_free(n); ++ } ++ ++ level = fnd->level; ++ ++ if (level) { ++ n = fnd->nodes[level - 1]; ++ e = fnd->de[level - 1]; ++ } else if (fnd->root_de) { ++ n = NULL; ++ e = fnd->root_de; ++ fnd->root_de = NULL; ++ } else { ++ *entry = NULL; ++ return 0; ++ } ++ ++ if (le16_to_cpu(e->size) > sizeof(struct NTFS_DE)) { ++ *entry = e; ++ if (!fnd->root_de) ++ fnd->root_de = e; ++ return 0; ++ } ++ } ++} ++ ++int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct INDEX_ROOT *root, struct NTFS_DE **entry, ++ size_t *off, struct ntfs_fnd *fnd) ++{ ++ int err; ++ struct indx_node *n = NULL; ++ struct NTFS_DE *e = NULL; ++ struct NTFS_DE *e2; ++ size_t bit; ++ CLST next_used_vbn; ++ CLST next_vbn; ++ u32 record_size = ni->mi.sbi->record_size; ++ ++ /* Use non sorted algorithm */ ++ if (!*entry) { ++ /* This is the first call */ ++ e = hdr_first_de(&root->ihdr); ++ if (!e) ++ return 0; ++ fnd_clear(fnd); ++ fnd->root_de = e; ++ ++ /* The first call with setup of initial element */ ++ if (*off >= record_size) { ++ next_vbn = (((*off - record_size) >> indx->index_bits)) ++ << indx->idx2vbn_bits; ++ /* jump inside cycle 'for'*/ ++ goto next; ++ } ++ ++ /* Start enumeration from root */ ++ *off = 0; ++ } else if (!fnd->root_de) ++ return -EINVAL; ++ ++ for (;;) { ++ /* Check if current entry can be used */ ++ if (e && le16_to_cpu(e->size) > sizeof(struct NTFS_DE)) ++ goto ok; ++ ++ if (!fnd->level) { ++ /* Continue to enumerate root */ ++ if (!de_is_last(fnd->root_de)) { ++ e = hdr_next_de(&root->ihdr, fnd->root_de); ++ if (!e) ++ return -EINVAL; ++ fnd->root_de = e; ++ continue; ++ } ++ ++ /* Start to enumerate indexes from 0 */ ++ next_vbn = 0; ++ } else { ++ /* Continue to enumerate indexes */ ++ e2 = fnd->de[fnd->level - 1]; ++ ++ n = fnd->nodes[fnd->level - 1]; ++ ++ if (!de_is_last(e2)) { ++ e = hdr_next_de(&n->index->ihdr, e2); ++ if (!e) ++ return -EINVAL; ++ fnd->de[fnd->level - 1] = e; ++ continue; ++ } ++ ++ /* Continue with next index */ ++ next_vbn = le64_to_cpu(n->index->vbn) + ++ root->index_block_clst; ++ } ++ ++next: ++ /* Release current index */ ++ if (n) { ++ fnd_pop(fnd); ++ put_indx_node(n); ++ n = NULL; ++ } ++ ++ /* Skip all free indexes */ ++ bit = next_vbn >> indx->idx2vbn_bits; ++ err = indx_used_bit(indx, ni, &bit); ++ if (err == -ENOENT || bit == MINUS_ONE_T) { ++ /* No used indexes */ ++ *entry = NULL; ++ return 0; ++ } ++ ++ next_used_vbn = bit << indx->idx2vbn_bits; ++ ++ /* Read buffer into memory */ ++ err = indx_read(indx, ni, next_used_vbn, &n); ++ if (err) ++ return err; ++ ++ e = hdr_first_de(&n->index->ihdr); ++ fnd_push(fnd, n, e); ++ if (!e) ++ return -EINVAL; ++ } ++ ++ok: ++ /* return offset to restore enumerator if necessary */ ++ if (!n) { ++ /* 'e' points in root */ ++ *off = PtrOffset(&root->ihdr, e); ++ } else { ++ /* 'e' points in index */ ++ *off = (le64_to_cpu(n->index->vbn) << indx->vbn2vbo_bits) + ++ record_size + PtrOffset(&n->index->ihdr, e); ++ } ++ ++ *entry = e; ++ return 0; ++} ++ ++/* ++ * indx_create_allocate ++ * ++ * create "Allocation + Bitmap" attributes ++ */ ++static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, ++ CLST *vbn) ++{ ++ int err = -ENOMEM; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTRIB *bitmap; ++ struct ATTRIB *alloc; ++ u32 data_size = 1u << indx->index_bits; ++ u32 alloc_size = ntfs_up_cluster(sbi, data_size); ++ CLST len = alloc_size >> sbi->cluster_bits; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ CLST alen; ++ struct runs_tree run; ++ ++ run_init(&run); ++ ++ err = attr_allocate_clusters(sbi, &run, 0, 0, len, NULL, 0, &alen, 0, ++ NULL); ++ if (err) ++ goto out; ++ ++ err = ni_insert_nonresident(ni, ATTR_ALLOC, in->name, in->name_len, ++ &run, 0, len, 0, &alloc, NULL); ++ if (err) ++ goto out1; ++ ++ alloc->nres.valid_size = alloc->nres.data_size = cpu_to_le64(data_size); ++ ++ err = ni_insert_resident(ni, bitmap_size(1), ATTR_BITMAP, in->name, ++ in->name_len, &bitmap, NULL); ++ if (err) ++ goto out2; ++ ++ if (in->name == I30_NAME) { ++ ni->vfs_inode.i_size = data_size; ++ inode_set_bytes(&ni->vfs_inode, alloc_size); ++ } ++ ++ memcpy(&indx->alloc_run, &run, sizeof(run)); ++ ++ *vbn = 0; ++ ++ return 0; ++ ++out2: ++ mi_remove_attr(&ni->mi, alloc); ++ ++out1: ++ run_deallocate(sbi, &run, false); ++ ++out: ++ return err; ++} ++ ++/* ++ * indx_add_allocate ++ * ++ * add clusters to index ++ */ ++static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, ++ CLST *vbn) ++{ ++ int err; ++ size_t bit; ++ u64 data_size; ++ u64 bmp_size, bmp_size_v; ++ struct ATTRIB *bmp, *alloc; ++ struct mft_inode *mi; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ ++ err = indx_find_free(indx, ni, &bit, &bmp); ++ if (err) ++ goto out1; ++ ++ if (bit != MINUS_ONE_T) { ++ bmp = NULL; ++ } else { ++ if (bmp->non_res) { ++ bmp_size = le64_to_cpu(bmp->nres.data_size); ++ bmp_size_v = le64_to_cpu(bmp->nres.valid_size); ++ } else { ++ bmp_size = bmp_size_v = le32_to_cpu(bmp->res.data_size); ++ } ++ ++ bit = bmp_size << 3; ++ } ++ ++ data_size = (u64)(bit + 1) << indx->index_bits; ++ ++ if (bmp) { ++ /* Increase bitmap */ ++ err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len, ++ &indx->bitmap_run, bitmap_size(bit + 1), ++ NULL, true, NULL); ++ if (err) ++ goto out1; ++ } ++ ++ alloc = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, in->name, in->name_len, ++ NULL, &mi); ++ if (!alloc) { ++ if (bmp) ++ goto out2; ++ goto out1; ++ } ++ ++ /* Increase allocation */ ++ err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len, ++ &indx->alloc_run, data_size, &data_size, true, ++ NULL); ++ if (err) { ++ if (bmp) ++ goto out2; ++ goto out1; ++ } ++ ++ *vbn = bit << indx->idx2vbn_bits; ++ ++ return 0; ++ ++out2: ++ /* Ops (no space?) */ ++ attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len, ++ &indx->bitmap_run, bmp_size, &bmp_size_v, false, NULL); ++ ++out1: ++ return err; ++} ++ ++/* ++ * indx_insert_into_root ++ * ++ * attempts to insert an entry into the index root ++ * If necessary, it will twiddle the index b-tree. ++ */ ++static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct NTFS_DE *new_de, ++ struct NTFS_DE *root_de, const void *ctx, ++ struct ntfs_fnd *fnd) ++{ ++ int err = 0; ++ struct NTFS_DE *e, *e0, *re; ++ struct mft_inode *mi; ++ struct ATTRIB *attr; ++ struct MFT_REC *rec; ++ struct INDEX_HDR *hdr; ++ struct indx_node *n; ++ CLST new_vbn; ++ __le64 *sub_vbn, t_vbn; ++ u16 new_de_size; ++ u32 hdr_used, hdr_total, asize, used, to_move; ++ u32 root_size, new_root_size; ++ struct ntfs_sb_info *sbi; ++ int ds_root; ++ struct INDEX_ROOT *root, *a_root = NULL; ++ ++ /* Get the record this root placed in */ ++ root = indx_get_root(indx, ni, &attr, &mi); ++ if (!root) ++ goto out; ++ ++ /* ++ * Try easy case: ++ * hdr_insert_de will succeed if there's room the root for the new entry. ++ */ ++ hdr = &root->ihdr; ++ sbi = ni->mi.sbi; ++ rec = mi->mrec; ++ used = le32_to_cpu(rec->used); ++ new_de_size = le16_to_cpu(new_de->size); ++ hdr_used = le32_to_cpu(hdr->used); ++ hdr_total = le32_to_cpu(hdr->total); ++ asize = le32_to_cpu(attr->size); ++ root_size = le32_to_cpu(attr->res.data_size); ++ ++ ds_root = new_de_size + hdr_used - hdr_total; ++ ++ if (used + ds_root < sbi->max_bytes_per_attr) { ++ /* make a room for new elements */ ++ mi_resize_attr(mi, attr, ds_root); ++ hdr->total = cpu_to_le32(hdr_total + ds_root); ++ e = hdr_insert_de(indx, hdr, new_de, root_de, ctx); ++ WARN_ON(!e); ++ fnd_clear(fnd); ++ fnd->root_de = e; ++ ++ return 0; ++ } ++ ++ /* Make a copy of root attribute to restore if error */ ++ a_root = ntfs_memdup(attr, asize); ++ if (!a_root) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* copy all the non-end entries from the index root to the new buffer.*/ ++ to_move = 0; ++ e0 = hdr_first_de(hdr); ++ ++ /* Calculate the size to copy */ ++ for (e = e0;; e = hdr_next_de(hdr, e)) { ++ if (!e) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (de_is_last(e)) ++ break; ++ to_move += le16_to_cpu(e->size); ++ } ++ ++ n = NULL; ++ if (!to_move) { ++ re = NULL; ++ } else { ++ re = ntfs_memdup(e0, to_move); ++ if (!re) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ sub_vbn = NULL; ++ if (de_has_vcn(e)) { ++ t_vbn = de_get_vbn_le(e); ++ sub_vbn = &t_vbn; ++ } ++ ++ new_root_size = sizeof(struct INDEX_ROOT) + sizeof(struct NTFS_DE) + ++ sizeof(u64); ++ ds_root = new_root_size - root_size; ++ ++ if (ds_root > 0 && used + ds_root > sbi->max_bytes_per_attr) { ++ /* make root external */ ++ err = -EOPNOTSUPP; ++ goto out; ++ } ++ ++ if (ds_root) ++ mi_resize_attr(mi, attr, ds_root); ++ ++ /* Fill first entry (vcn will be set later) */ ++ e = (struct NTFS_DE *)(root + 1); ++ memset(e, 0, sizeof(struct NTFS_DE)); ++ e->size = cpu_to_le16(sizeof(struct NTFS_DE) + sizeof(u64)); ++ e->flags = NTFS_IE_HAS_SUBNODES | NTFS_IE_LAST; ++ ++ hdr->flags = 1; ++ hdr->used = hdr->total = ++ cpu_to_le32(new_root_size - offsetof(struct INDEX_ROOT, ihdr)); ++ ++ fnd->root_de = hdr_first_de(hdr); ++ mi->dirty = true; ++ ++ /* Create alloc and bitmap attributes (if not) */ ++ err = run_is_empty(&indx->alloc_run) ++ ? indx_create_allocate(indx, ni, &new_vbn) ++ : indx_add_allocate(indx, ni, &new_vbn); ++ ++ /* layout of record may be changed, so rescan root */ ++ root = indx_get_root(indx, ni, &attr, &mi); ++ if (!root) { ++ /* bug? */ ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ err = -EINVAL; ++ goto out1; ++ } ++ ++ if (err) { ++ /* restore root */ ++ if (mi_resize_attr(mi, attr, -ds_root)) ++ memcpy(attr, a_root, asize); ++ else { ++ /* bug? */ ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ } ++ goto out1; ++ } ++ ++ e = (struct NTFS_DE *)(root + 1); ++ *(__le64 *)(e + 1) = cpu_to_le64(new_vbn); ++ mi->dirty = true; ++ ++ /* now we can create/format the new buffer and copy the entries into */ ++ n = indx_new(indx, ni, new_vbn, sub_vbn); ++ if (IS_ERR(n)) { ++ err = PTR_ERR(n); ++ goto out1; ++ } ++ ++ hdr = &n->index->ihdr; ++ hdr_used = le32_to_cpu(hdr->used); ++ hdr_total = le32_to_cpu(hdr->total); ++ ++ /* Copy root entries into new buffer */ ++ hdr_insert_head(hdr, re, to_move); ++ ++ /* Update bitmap attribute */ ++ indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits); ++ ++ /* Check if we can insert new entry new index buffer */ ++ if (hdr_used + new_de_size > hdr_total) { ++ /* ++ * This occurs if mft record is the same or bigger than index ++ * buffer. Move all root new index and have no space to add ++ * new entry classic case when mft record is 1K and index ++ * buffer 4K the problem should not occurs ++ */ ++ ntfs_free(re); ++ indx_write(indx, ni, n, 0); ++ ++ put_indx_node(n); ++ fnd_clear(fnd); ++ err = indx_insert_entry(indx, ni, new_de, ctx, fnd); ++ goto out; ++ } ++ ++ /* ++ * Now root is a parent for new index buffer ++ * Insert NewEntry a new buffer ++ */ ++ e = hdr_insert_de(indx, hdr, new_de, NULL, ctx); ++ if (!e) { ++ err = -EINVAL; ++ goto out1; ++ } ++ fnd_push(fnd, n, e); ++ ++ /* Just write updates index into disk */ ++ indx_write(indx, ni, n, 0); ++ ++ n = NULL; ++ ++out1: ++ ntfs_free(re); ++ if (n) ++ put_indx_node(n); ++ ++out: ++ ntfs_free(a_root); ++ return err; ++} ++ ++/* ++ * indx_insert_into_buffer ++ * ++ * attempts to insert an entry into an Index Allocation Buffer. ++ * If necessary, it will split the buffer. ++ */ ++static int ++indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, ++ struct INDEX_ROOT *root, const struct NTFS_DE *new_de, ++ const void *ctx, int level, struct ntfs_fnd *fnd) ++{ ++ int err; ++ const struct NTFS_DE *sp; ++ struct NTFS_DE *e, *de_t, *up_e = NULL; ++ struct indx_node *n2 = NULL; ++ struct indx_node *n1 = fnd->nodes[level]; ++ struct INDEX_HDR *hdr1 = &n1->index->ihdr; ++ struct INDEX_HDR *hdr2; ++ u32 to_copy, used; ++ CLST new_vbn; ++ __le64 t_vbn, *sub_vbn; ++ u16 sp_size; ++ ++ /* Try the most easy case */ ++ e = fnd->level - 1 == level ? fnd->de[level] : NULL; ++ e = hdr_insert_de(indx, hdr1, new_de, e, ctx); ++ fnd->de[level] = e; ++ if (e) { ++ /* Just write updated index into disk */ ++ indx_write(indx, ni, n1, 0); ++ return 0; ++ } ++ ++ /* ++ * No space to insert into buffer. Split it. ++ * To split we: ++ * - Save split point ('cause index buffers will be changed) ++ * - Allocate NewBuffer and copy all entries <= sp into new buffer ++ * - Remove all entries (sp including) from TargetBuffer ++ * - Insert NewEntry into left or right buffer (depending on sp <=> ++ * NewEntry) ++ * - Insert sp into parent buffer (or root) ++ * - Make sp a parent for new buffer ++ */ ++ sp = hdr_find_split(hdr1); ++ if (!sp) ++ return -EINVAL; ++ ++ sp_size = le16_to_cpu(sp->size); ++ up_e = ntfs_malloc(sp_size + sizeof(u64)); ++ if (!up_e) ++ return -ENOMEM; ++ memcpy(up_e, sp, sp_size); ++ ++ if (!hdr1->flags) { ++ up_e->flags |= NTFS_IE_HAS_SUBNODES; ++ up_e->size = cpu_to_le16(sp_size + sizeof(u64)); ++ sub_vbn = NULL; ++ } else { ++ t_vbn = de_get_vbn_le(up_e); ++ sub_vbn = &t_vbn; ++ } ++ ++ /* Allocate on disk a new index allocation buffer. */ ++ err = indx_add_allocate(indx, ni, &new_vbn); ++ if (err) ++ goto out; ++ ++ /* Allocate and format memory a new index buffer */ ++ n2 = indx_new(indx, ni, new_vbn, sub_vbn); ++ if (IS_ERR(n2)) { ++ err = PTR_ERR(n2); ++ goto out; ++ } ++ ++ hdr2 = &n2->index->ihdr; ++ ++ /* Make sp a parent for new buffer */ ++ de_set_vbn(up_e, new_vbn); ++ ++ /* copy all the entries <= sp into the new buffer. */ ++ de_t = hdr_first_de(hdr1); ++ to_copy = PtrOffset(de_t, sp); ++ hdr_insert_head(hdr2, de_t, to_copy); ++ ++ /* remove all entries (sp including) from hdr1 */ ++ used = le32_to_cpu(hdr1->used) - to_copy - sp_size; ++ memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off)); ++ hdr1->used = cpu_to_le32(used); ++ ++ /* Insert new entry into left or right buffer (depending on sp <=> new_de) */ ++ hdr_insert_de(indx, ++ (*indx->cmp)(new_de + 1, le16_to_cpu(new_de->key_size), ++ up_e + 1, le16_to_cpu(up_e->key_size), ++ ctx) < 0 ++ ? hdr2 ++ : hdr1, ++ new_de, NULL, ctx); ++ ++ indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits); ++ ++ indx_write(indx, ni, n1, 0); ++ indx_write(indx, ni, n2, 0); ++ ++ put_indx_node(n2); ++ ++ /* ++ * we've finished splitting everybody, so we are ready to ++ * insert the promoted entry into the parent. ++ */ ++ if (!level) { ++ /* Insert in root */ ++ err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd); ++ if (err) ++ goto out; ++ } else { ++ /* ++ * The target buffer's parent is another index buffer ++ * TODO: Remove recursion ++ */ ++ err = indx_insert_into_buffer(indx, ni, root, up_e, ctx, ++ level - 1, fnd); ++ if (err) ++ goto out; ++ } ++ ++out: ++ ntfs_free(up_e); ++ ++ return err; ++} ++ ++/* ++ * indx_insert_entry ++ * ++ * inserts new entry into index ++ */ ++int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct NTFS_DE *new_de, const void *ctx, ++ struct ntfs_fnd *fnd) ++{ ++ int err; ++ int diff; ++ struct NTFS_DE *e; ++ struct ntfs_fnd *fnd_a = NULL; ++ struct INDEX_ROOT *root; ++ ++ if (!fnd) { ++ fnd_a = fnd_get(); ++ if (!fnd_a) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ fnd = fnd_a; ++ } ++ ++ root = indx_get_root(indx, ni, NULL, NULL); ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (fnd_is_empty(fnd)) { ++ /* Find the spot the tree where we want to insert the new entry. */ ++ err = indx_find(indx, ni, root, new_de + 1, ++ le16_to_cpu(new_de->key_size), ctx, &diff, &e, ++ fnd); ++ if (err) ++ goto out; ++ ++ if (!diff) { ++ err = -EEXIST; ++ goto out; ++ } ++ } ++ ++ if (!fnd->level) { ++ /* The root is also a leaf, so we'll insert the new entry into it. */ ++ err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx, ++ fnd); ++ if (err) ++ goto out; ++ } else { ++ /* found a leaf buffer, so we'll insert the new entry into it.*/ ++ err = indx_insert_into_buffer(indx, ni, root, new_de, ctx, ++ fnd->level - 1, fnd); ++ if (err) ++ goto out; ++ } ++ ++out: ++ fnd_put(fnd_a); ++out1: ++ return err; ++} ++ ++/* ++ * indx_find_buffer ++ * ++ * locates a buffer the tree. ++ */ ++static struct indx_node *indx_find_buffer(struct ntfs_index *indx, ++ struct ntfs_inode *ni, ++ const struct INDEX_ROOT *root, ++ __le64 vbn, struct indx_node *n) ++{ ++ int err; ++ const struct NTFS_DE *e; ++ struct indx_node *r; ++ const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; ++ ++ /* Step 1: Scan one level */ ++ for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { ++ if (!e) ++ return ERR_PTR(-EINVAL); ++ ++ if (de_has_vcn(e) && vbn == de_get_vbn_le(e)) ++ return n; ++ ++ if (de_is_last(e)) ++ break; ++ } ++ ++ /* Step2: Do recursion */ ++ e = Add2Ptr(hdr, le32_to_cpu(hdr->de_off)); ++ for (;;) { ++ if (de_has_vcn_ex(e)) { ++ err = indx_read(indx, ni, de_get_vbn(e), &n); ++ if (err) ++ return ERR_PTR(err); ++ ++ r = indx_find_buffer(indx, ni, root, vbn, n); ++ if (r) ++ return r; ++ } ++ ++ if (de_is_last(e)) ++ break; ++ ++ e = Add2Ptr(e, le16_to_cpu(e->size)); ++ } ++ ++ return NULL; ++} ++ ++/* ++ * indx_shrink ++ * ++ * deallocates unused tail indexes ++ */ ++static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni, ++ size_t bit) ++{ ++ int err = 0; ++ u64 bpb, new_data; ++ size_t nbits; ++ struct ATTRIB *b; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ const struct INDEX_NAMES *in = &s_index_names[indx->type]; ++ ++ b = ni_find_attr(ni, NULL, &le, ATTR_BITMAP, in->name, in->name_len, ++ NULL, NULL); ++ ++ if (!b) ++ return -ENOENT; ++ ++ if (!b->non_res) { ++ unsigned long pos; ++ const unsigned long *bm = resident_data(b); ++ ++ nbits = le32_to_cpu(b->res.data_size) * 8; ++ ++ if (bit >= nbits) ++ return 0; ++ ++ pos = find_next_bit(bm, nbits, bit); ++ if (pos < nbits) ++ return 0; ++ } else { ++ size_t used = MINUS_ONE_T; ++ ++ nbits = le64_to_cpu(b->nres.data_size) * 8; ++ ++ if (bit >= nbits) ++ return 0; ++ ++ err = scan_nres_bitmap(ni, b, indx, bit, &scan_for_used, &used); ++ if (err) ++ return err; ++ ++ if (used != MINUS_ONE_T) ++ return 0; ++ } ++ ++ new_data = (u64)bit << indx->index_bits; ++ ++ err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len, ++ &indx->alloc_run, new_data, &new_data, false, NULL); ++ if (err) ++ return err; ++ ++ bpb = bitmap_size(bit); ++ if (bpb * 8 == nbits) ++ return 0; ++ ++ err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len, ++ &indx->bitmap_run, bpb, &bpb, false, NULL); ++ ++ return err; ++} ++ ++static int indx_free_children(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const struct NTFS_DE *e, bool trim) ++{ ++ int err; ++ struct indx_node *n; ++ struct INDEX_HDR *hdr; ++ CLST vbn = de_get_vbn(e); ++ size_t i; ++ ++ err = indx_read(indx, ni, vbn, &n); ++ if (err) ++ return err; ++ ++ hdr = &n->index->ihdr; ++ /* First, recurse into the children, if any.*/ ++ if (hdr_has_subnode(hdr)) { ++ for (e = hdr_first_de(hdr); e; e = hdr_next_de(hdr, e)) { ++ indx_free_children(indx, ni, e, false); ++ if (de_is_last(e)) ++ break; ++ } ++ } ++ ++ put_indx_node(n); ++ ++ i = vbn >> indx->idx2vbn_bits; ++ /* We've gotten rid of the children; add this buffer to the free list. */ ++ indx_mark_free(indx, ni, i); ++ ++ if (!trim) ++ return 0; ++ ++ /* ++ * If there are no used indexes after current free index ++ * then we can truncate allocation and bitmap ++ * Use bitmap to estimate the case ++ */ ++ indx_shrink(indx, ni, i + 1); ++ return 0; ++} ++ ++/* ++ * indx_get_entry_to_replace ++ * ++ * finds a replacement entry for a deleted entry ++ * always returns a node entry: ++ * NTFS_IE_HAS_SUBNODES is set the flags and the size includes the sub_vcn ++ */ ++static int indx_get_entry_to_replace(struct ntfs_index *indx, ++ struct ntfs_inode *ni, ++ const struct NTFS_DE *de_next, ++ struct NTFS_DE **de_to_replace, ++ struct ntfs_fnd *fnd) ++{ ++ int err; ++ int level = -1; ++ CLST vbn; ++ struct NTFS_DE *e, *te, *re; ++ struct indx_node *n; ++ struct INDEX_BUFFER *ib; ++ ++ *de_to_replace = NULL; ++ ++ /* Find first leaf entry down from de_next */ ++ vbn = de_get_vbn(de_next); ++ for (;;) { ++ n = NULL; ++ err = indx_read(indx, ni, vbn, &n); ++ if (err) ++ goto out; ++ ++ e = hdr_first_de(&n->index->ihdr); ++ fnd_push(fnd, n, e); ++ ++ if (!de_is_last(e)) { ++ /* ++ * This buffer is non-empty, so its first entry could be used as the ++ * replacement entry. ++ */ ++ level = fnd->level - 1; ++ } ++ ++ if (!de_has_vcn(e)) ++ break; ++ ++ /* This buffer is a node. Continue to go down */ ++ vbn = de_get_vbn(e); ++ } ++ ++ if (level == -1) ++ goto out; ++ ++ n = fnd->nodes[level]; ++ te = hdr_first_de(&n->index->ihdr); ++ /* Copy the candidate entry into the replacement entry buffer. */ ++ re = ntfs_malloc(le16_to_cpu(te->size) + sizeof(u64)); ++ if (!re) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ *de_to_replace = re; ++ memcpy(re, te, le16_to_cpu(te->size)); ++ ++ if (!de_has_vcn(re)) { ++ /* ++ * The replacement entry we found doesn't have a sub_vcn. increase its size ++ * to hold one. ++ */ ++ le16_add_cpu(&re->size, sizeof(u64)); ++ re->flags |= NTFS_IE_HAS_SUBNODES; ++ } else { ++ /* ++ * The replacement entry we found was a node entry, which means that all ++ * its child buffers are empty. Return them to the free pool. ++ */ ++ indx_free_children(indx, ni, te, true); ++ } ++ ++ /* ++ * Expunge the replacement entry from its former location, ++ * and then write that buffer. ++ */ ++ ib = n->index; ++ e = hdr_delete_de(&ib->ihdr, te); ++ ++ fnd->de[level] = e; ++ indx_write(indx, ni, n, 0); ++ ++ /* Check to see if this action created an empty leaf. */ ++ if (ib_is_leaf(ib) && ib_is_empty(ib)) ++ return 0; ++ ++out: ++ fnd_clear(fnd); ++ return err; ++} ++ ++/* ++ * indx_delete_entry ++ * ++ * deletes an entry from the index. ++ */ ++int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, ++ const void *key, u32 key_len, const void *ctx) ++{ ++ int err, diff; ++ struct INDEX_ROOT *root; ++ struct INDEX_HDR *hdr; ++ struct ntfs_fnd *fnd, *fnd2; ++ struct INDEX_BUFFER *ib; ++ struct NTFS_DE *e, *re, *next, *prev, *me; ++ struct indx_node *n, *n2d = NULL; ++ __le64 sub_vbn; ++ int level, level2; ++ struct ATTRIB *attr; ++ struct mft_inode *mi; ++ u32 e_size, root_size, new_root_size; ++ size_t trim_bit; ++ const struct INDEX_NAMES *in; ++ ++ fnd = fnd_get(); ++ if (!fnd) { ++ err = -ENOMEM; ++ goto out2; ++ } ++ ++ fnd2 = fnd_get(); ++ if (!fnd2) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ root = indx_get_root(indx, ni, &attr, &mi); ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Locate the entry to remove. */ ++ err = indx_find(indx, ni, root, key, key_len, ctx, &diff, &e, fnd); ++ if (err) ++ goto out; ++ ++ if (!e || diff) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ level = fnd->level; ++ ++ if (level) { ++ n = fnd->nodes[level - 1]; ++ e = fnd->de[level - 1]; ++ ib = n->index; ++ hdr = &ib->ihdr; ++ } else { ++ hdr = &root->ihdr; ++ e = fnd->root_de; ++ n = NULL; ++ } ++ ++ e_size = le16_to_cpu(e->size); ++ ++ if (!de_has_vcn_ex(e)) { ++ /* The entry to delete is a leaf, so we can just rip it out */ ++ hdr_delete_de(hdr, e); ++ ++ if (!level) { ++ hdr->total = hdr->used; ++ ++ /* Shrink resident root attribute */ ++ mi_resize_attr(mi, attr, 0 - e_size); ++ goto out; ++ } ++ ++ indx_write(indx, ni, n, 0); ++ ++ /* ++ * Check to see if removing that entry made ++ * the leaf empty. ++ */ ++ if (ib_is_leaf(ib) && ib_is_empty(ib)) { ++ fnd_pop(fnd); ++ fnd_push(fnd2, n, e); ++ } ++ } else { ++ /* ++ * The entry we wish to delete is a node buffer, so we ++ * have to find a replacement for it. ++ */ ++ next = de_get_next(e); ++ ++ err = indx_get_entry_to_replace(indx, ni, next, &re, fnd2); ++ if (err) ++ goto out; ++ ++ if (re) { ++ de_set_vbn_le(re, de_get_vbn_le(e)); ++ hdr_delete_de(hdr, e); ++ ++ err = level ? indx_insert_into_buffer(indx, ni, root, ++ re, ctx, ++ fnd->level - 1, ++ fnd) ++ : indx_insert_into_root(indx, ni, re, e, ++ ctx, fnd); ++ ntfs_free(re); ++ ++ if (err) ++ goto out; ++ } else { ++ /* ++ * There is no replacement for the current entry. ++ * This means that the subtree rooted at its node is empty, ++ * and can be deleted, which turn means that the node can ++ * just inherit the deleted entry sub_vcn ++ */ ++ indx_free_children(indx, ni, next, true); ++ ++ de_set_vbn_le(next, de_get_vbn_le(e)); ++ hdr_delete_de(hdr, e); ++ if (level) { ++ indx_write(indx, ni, n, 0); ++ } else { ++ hdr->total = hdr->used; ++ ++ /* Shrink resident root attribute */ ++ mi_resize_attr(mi, attr, 0 - e_size); ++ } ++ } ++ } ++ ++ /* Delete a branch of tree */ ++ if (!fnd2 || !fnd2->level) ++ goto out; ++ ++ /* Reinit root 'cause it can be changed */ ++ root = indx_get_root(indx, ni, &attr, &mi); ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ n2d = NULL; ++ sub_vbn = fnd2->nodes[0]->index->vbn; ++ level2 = 0; ++ level = fnd->level; ++ ++ hdr = level ? &fnd->nodes[level - 1]->index->ihdr : &root->ihdr; ++ ++ /* Scan current level */ ++ for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { ++ if (!e) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (de_has_vcn(e) && sub_vbn == de_get_vbn_le(e)) ++ break; ++ ++ if (de_is_last(e)) { ++ e = NULL; ++ break; ++ } ++ } ++ ++ if (!e) { ++ /* Do slow search from root */ ++ struct indx_node *in; ++ ++ fnd_clear(fnd); ++ ++ in = indx_find_buffer(indx, ni, root, sub_vbn, NULL); ++ if (IS_ERR(in)) { ++ err = PTR_ERR(in); ++ goto out; ++ } ++ ++ if (in) ++ fnd_push(fnd, in, NULL); ++ } ++ ++ /* Merge fnd2 -> fnd */ ++ for (level = 0; level < fnd2->level; level++) { ++ fnd_push(fnd, fnd2->nodes[level], fnd2->de[level]); ++ fnd2->nodes[level] = NULL; ++ } ++ fnd2->level = 0; ++ ++ hdr = NULL; ++ for (level = fnd->level; level; level--) { ++ struct indx_node *in = fnd->nodes[level - 1]; ++ ++ ib = in->index; ++ if (ib_is_empty(ib)) { ++ sub_vbn = ib->vbn; ++ } else { ++ hdr = &ib->ihdr; ++ n2d = in; ++ level2 = level; ++ break; ++ } ++ } ++ ++ if (!hdr) ++ hdr = &root->ihdr; ++ ++ e = hdr_first_de(hdr); ++ if (!e) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (hdr != &root->ihdr || !de_is_last(e)) { ++ prev = NULL; ++ while (!de_is_last(e)) { ++ if (de_has_vcn(e) && sub_vbn == de_get_vbn_le(e)) ++ break; ++ prev = e; ++ e = hdr_next_de(hdr, e); ++ if (!e) { ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ if (sub_vbn != de_get_vbn_le(e)) { ++ /* ++ * Didn't find the parent entry, although this buffer is the parent trail. ++ * Something is corrupt. ++ */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (de_is_last(e)) { ++ /* ++ * Since we can't remove the end entry, we'll remove its ++ * predecessor instead. This means we have to transfer the ++ * predecessor's sub_vcn to the end entry. ++ * Note: that this index block is not empty, so the ++ * predecessor must exist ++ */ ++ if (!prev) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (de_has_vcn(prev)) { ++ de_set_vbn_le(e, de_get_vbn_le(prev)); ++ } else if (de_has_vcn(e)) { ++ le16_sub_cpu(&e->size, sizeof(u64)); ++ e->flags &= ~NTFS_IE_HAS_SUBNODES; ++ le32_sub_cpu(&hdr->used, sizeof(u64)); ++ } ++ e = prev; ++ } ++ ++ /* ++ * Copy the current entry into a temporary buffer (stripping off its ++ * down-pointer, if any) and delete it from the current buffer or root, ++ * as appropriate. ++ */ ++ e_size = le16_to_cpu(e->size); ++ me = ntfs_memdup(e, e_size); ++ if (!me) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (de_has_vcn(me)) { ++ me->flags &= ~NTFS_IE_HAS_SUBNODES; ++ le16_sub_cpu(&me->size, sizeof(u64)); ++ } ++ ++ hdr_delete_de(hdr, e); ++ ++ if (hdr == &root->ihdr) { ++ level = 0; ++ hdr->total = hdr->used; ++ ++ /* Shrink resident root attribute */ ++ mi_resize_attr(mi, attr, 0 - e_size); ++ } else { ++ indx_write(indx, ni, n2d, 0); ++ level = level2; ++ } ++ ++ /* Mark unused buffers as free */ ++ trim_bit = -1; ++ for (; level < fnd->level; level++) { ++ ib = fnd->nodes[level]->index; ++ if (ib_is_empty(ib)) { ++ size_t k = le64_to_cpu(ib->vbn) >> ++ indx->idx2vbn_bits; ++ ++ indx_mark_free(indx, ni, k); ++ if (k < trim_bit) ++ trim_bit = k; ++ } ++ } ++ ++ fnd_clear(fnd); ++ /*fnd->root_de = NULL;*/ ++ ++ /* ++ * Re-insert the entry into the tree. ++ * Find the spot the tree where we want to insert the new entry. ++ */ ++ err = indx_insert_entry(indx, ni, me, ctx, fnd); ++ ntfs_free(me); ++ if (err) ++ goto out; ++ ++ if (trim_bit != -1) ++ indx_shrink(indx, ni, trim_bit); ++ } else { ++ /* ++ * This tree needs to be collapsed down to an empty root. ++ * Recreate the index root as an empty leaf and free all the bits the ++ * index allocation bitmap. ++ */ ++ fnd_clear(fnd); ++ fnd_clear(fnd2); ++ ++ in = &s_index_names[indx->type]; ++ ++ err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len, ++ &indx->alloc_run, 0, NULL, false, NULL); ++ err = ni_remove_attr(ni, ATTR_ALLOC, in->name, in->name_len, ++ false, NULL); ++ run_close(&indx->alloc_run); ++ ++ err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len, ++ &indx->bitmap_run, 0, NULL, false, NULL); ++ err = ni_remove_attr(ni, ATTR_BITMAP, in->name, in->name_len, ++ false, NULL); ++ run_close(&indx->bitmap_run); ++ ++ root = indx_get_root(indx, ni, &attr, &mi); ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ root_size = le32_to_cpu(attr->res.data_size); ++ new_root_size = ++ sizeof(struct INDEX_ROOT) + sizeof(struct NTFS_DE); ++ ++ if (new_root_size != root_size && ++ !mi_resize_attr(mi, attr, new_root_size - root_size)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Fill first entry */ ++ e = (struct NTFS_DE *)(root + 1); ++ e->ref.low = 0; ++ e->ref.high = 0; ++ e->ref.seq = 0; ++ e->size = cpu_to_le16(sizeof(struct NTFS_DE)); ++ e->flags = NTFS_IE_LAST; // 0x02 ++ e->key_size = 0; ++ e->res = 0; ++ ++ hdr = &root->ihdr; ++ hdr->flags = 0; ++ hdr->used = hdr->total = cpu_to_le32( ++ new_root_size - offsetof(struct INDEX_ROOT, ihdr)); ++ mi->dirty = true; ++ } ++ ++out: ++ fnd_put(fnd2); ++out1: ++ fnd_put(fnd); ++out2: ++ return err; ++} ++ ++/* ++ * Update duplicated information in directory entry ++ * 'dup' - info from MFT record ++ */ ++int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, ++ const struct ATTR_FILE_NAME *fname, ++ const struct NTFS_DUP_INFO *dup, int sync) ++{ ++ int err, diff; ++ struct NTFS_DE *e = NULL; ++ struct ATTR_FILE_NAME *e_fname; ++ struct ntfs_fnd *fnd; ++ struct INDEX_ROOT *root; ++ struct mft_inode *mi; ++ struct ntfs_index *indx = &ni->dir; ++ ++ fnd = fnd_get(); ++ if (!fnd) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ root = indx_get_root(indx, ni, NULL, &mi); ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Find entry in directory */ ++ err = indx_find(indx, ni, root, fname, fname_full_size(fname), sbi, ++ &diff, &e, fnd); ++ if (err) ++ goto out; ++ ++ if (!e) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (diff) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ e_fname = (struct ATTR_FILE_NAME *)(e + 1); ++ ++ if (!memcmp(&e_fname->dup, dup, sizeof(*dup))) { ++ /* nothing to update in index! Try to avoid this call */ ++ goto out; ++ } ++ ++ memcpy(&e_fname->dup, dup, sizeof(*dup)); ++ ++ if (fnd->level) { ++ /* directory entry in index */ ++ err = indx_write(indx, ni, fnd->nodes[fnd->level - 1], sync); ++ } else { ++ /* directory entry in directory MFT record */ ++ mi->dirty = true; ++ if (sync) ++ err = mi_write(mi, 1); ++ else ++ mark_inode_dirty(&ni->vfs_inode); ++ } ++ ++out: ++ fnd_put(fnd); ++ ++out1: ++ return err; ++} +diff --git a/src/inode.c b/src/inode.c +new file mode 100644 +index 0000000000000000000000000000000000000000..bf51e294432efebf20f75821fb25d2048eb7a15b +--- /dev/null ++++ b/src/inode.c +@@ -0,0 +1,2029 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* ++ * ntfs_read_mft ++ * ++ * reads record and parses MFT ++ */ ++static struct inode *ntfs_read_mft(struct inode *inode, ++ const struct cpu_str *name, ++ const struct MFT_REF *ref) ++{ ++ int err = 0; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ mode_t mode = 0; ++ struct ATTR_STD_INFO5 *std5 = NULL; ++ struct ATTR_LIST_ENTRY *le; ++ struct ATTRIB *attr; ++ bool is_match = false; ++ bool is_root = false; ++ bool is_dir; ++ unsigned long ino = inode->i_ino; ++ u32 rp_fa = 0, asize, t32; ++ u16 roff, rsize, names = 0; ++ const struct ATTR_FILE_NAME *fname = NULL; ++ const struct INDEX_ROOT *root; ++ struct REPARSE_DATA_BUFFER rp; // 0x18 bytes ++ u64 t64; ++ struct MFT_REC *rec; ++ struct runs_tree *run; ++ ++ inode->i_op = NULL; ++ /* Setup 'uid' and 'gid' */ ++ inode->i_uid = sbi->options.fs_uid; ++ inode->i_gid = sbi->options.fs_gid; ++ ++ err = mi_init(&ni->mi, sbi, ino); ++ if (err) ++ goto out; ++ ++ if (!sbi->mft.ni && ino == MFT_REC_MFT && !sb->s_root) { ++ t64 = sbi->mft.lbo >> sbi->cluster_bits; ++ t32 = bytes_to_cluster(sbi, MFT_REC_VOL * sbi->record_size); ++ sbi->mft.ni = ni; ++ init_rwsem(&ni->file.run_lock); ++ ++ if (!run_add_entry(&ni->file.run, 0, t64, t32, true)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ err = mi_read(&ni->mi, ino == MFT_REC_MFT); ++ ++ if (err) ++ goto out; ++ ++ rec = ni->mi.mrec; ++ ++ if (sbi->flags & NTFS_FLAGS_LOG_REPLAYING) { ++ ; ++ } else if (ref->seq != rec->seq) { ++ err = -EINVAL; ++ ntfs_err(sb, "MFT: r=%lx, expect seq=%x instead of %x!", ino, ++ le16_to_cpu(ref->seq), le16_to_cpu(rec->seq)); ++ goto out; ++ } else if (!is_rec_inuse(rec)) { ++ err = -EINVAL; ++ ntfs_err(sb, "Inode r=%x is not in use!", (u32)ino); ++ goto out; ++ } ++ ++ if (le32_to_cpu(rec->total) != sbi->record_size) { ++ // bad inode? ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (!is_rec_base(rec)) ++ goto Ok; ++ ++ /* record should contain $I30 root */ ++ is_dir = rec->flags & RECORD_FLAG_DIR; ++ ++ inode->i_generation = le16_to_cpu(rec->seq); ++ ++ /* Enumerate all struct Attributes MFT */ ++ le = NULL; ++ attr = NULL; ++ ++ /* ++ * to reduce tab pressure use goto instead of ++ * while( (attr = ni_enum_attr_ex(ni, attr, &le, NULL) )) ++ */ ++next_attr: ++ run = NULL; ++ err = -EINVAL; ++ attr = ni_enum_attr_ex(ni, attr, &le, NULL); ++ if (!attr) ++ goto end_enum; ++ ++ if (le && le->vcn) { ++ /* This is non primary attribute segment. Ignore if not MFT */ ++ if (ino != MFT_REC_MFT || attr->type != ATTR_DATA) ++ goto next_attr; ++ ++ run = &ni->file.run; ++ asize = le32_to_cpu(attr->size); ++ goto attr_unpack_run; ++ } ++ ++ roff = attr->non_res ? 0 : le16_to_cpu(attr->res.data_off); ++ rsize = attr->non_res ? 0 : le32_to_cpu(attr->res.data_size); ++ asize = le32_to_cpu(attr->size); ++ ++ switch (attr->type) { ++ case ATTR_STD: ++ if (attr->non_res || ++ asize < sizeof(struct ATTR_STD_INFO) + roff || ++ rsize < sizeof(struct ATTR_STD_INFO)) ++ goto out; ++ ++ if (std5) ++ goto next_attr; ++ ++ std5 = Add2Ptr(attr, roff); ++ ++#ifdef STATX_BTIME ++ nt2kernel(std5->cr_time, &ni->i_crtime); ++#endif ++ nt2kernel(std5->a_time, &inode->i_atime); ++ nt2kernel(std5->c_time, &inode->i_ctime); ++ nt2kernel(std5->m_time, &inode->i_mtime); ++ ++ ni->std_fa = std5->fa; ++ ++ if (asize >= sizeof(struct ATTR_STD_INFO5) + roff && ++ rsize >= sizeof(struct ATTR_STD_INFO5)) ++ ni->std_security_id = std5->security_id; ++ goto next_attr; ++ ++ case ATTR_LIST: ++ if (attr->name_len || le || ino == MFT_REC_LOG) ++ goto out; ++ ++ err = ntfs_load_attr_list(ni, attr); ++ if (err) ++ goto out; ++ ++ le = NULL; ++ attr = NULL; ++ goto next_attr; ++ ++ case ATTR_NAME: ++ if (attr->non_res || asize < SIZEOF_ATTRIBUTE_FILENAME + roff || ++ rsize < SIZEOF_ATTRIBUTE_FILENAME) ++ goto out; ++ ++ fname = Add2Ptr(attr, roff); ++ if (fname->type == FILE_NAME_DOS) ++ goto next_attr; ++ ++ names += 1; ++ if (name && name->len == fname->name_len && ++ !ntfs_cmp_names_cpu(name, (struct le_str *)&fname->name_len, ++ NULL, false)) ++ is_match = true; ++ ++ goto next_attr; ++ ++ case ATTR_DATA: ++ if (is_dir) { ++ /* ignore data attribute in dir record */ ++ goto next_attr; ++ } ++ ++ if (ino == MFT_REC_BADCLUST && !attr->non_res) ++ goto next_attr; ++ ++ if (attr->name_len && ++ ((ino != MFT_REC_BADCLUST || !attr->non_res || ++ attr->name_len != ARRAY_SIZE(BAD_NAME) || ++ memcmp(attr_name(attr), BAD_NAME, sizeof(BAD_NAME))) && ++ (ino != MFT_REC_SECURE || !attr->non_res || ++ attr->name_len != ARRAY_SIZE(SDS_NAME) || ++ memcmp(attr_name(attr), SDS_NAME, sizeof(SDS_NAME))))) { ++ /* file contains stream attribute. ignore it */ ++ goto next_attr; ++ } ++ ++ if (is_attr_sparsed(attr)) ++ ni->std_fa |= FILE_ATTRIBUTE_SPARSE_FILE; ++ else ++ ni->std_fa &= ~FILE_ATTRIBUTE_SPARSE_FILE; ++ ++ if (is_attr_compressed(attr)) ++ ni->std_fa |= FILE_ATTRIBUTE_COMPRESSED; ++ else ++ ni->std_fa &= ~FILE_ATTRIBUTE_COMPRESSED; ++ ++ if (is_attr_encrypted(attr)) ++ ni->std_fa |= FILE_ATTRIBUTE_ENCRYPTED; ++ else ++ ni->std_fa &= ~FILE_ATTRIBUTE_ENCRYPTED; ++ ++ if (!attr->non_res) { ++ ni->i_valid = inode->i_size = rsize; ++ inode_set_bytes(inode, rsize); ++ t32 = asize; ++ } else { ++ t32 = le16_to_cpu(attr->nres.run_off); ++ } ++ ++ mode = S_IFREG | (0777 & sbi->options.fs_fmask_inv); ++ ++ if (!attr->non_res) { ++ ni->ni_flags |= NI_FLAG_RESIDENT; ++ goto next_attr; ++ } ++ ++ inode_set_bytes(inode, attr_ondisk_size(attr)); ++ ++ ni->i_valid = le64_to_cpu(attr->nres.valid_size); ++ inode->i_size = le64_to_cpu(attr->nres.data_size); ++ if (!attr->nres.alloc_size) ++ goto next_attr; ++ ++ run = ino == MFT_REC_BITMAP ? &sbi->used.bitmap.run ++ : &ni->file.run; ++ break; ++ ++ case ATTR_ROOT: ++ if (attr->non_res) ++ goto out; ++ ++ root = Add2Ptr(attr, roff); ++ is_root = true; ++ ++ if (attr->name_len != ARRAY_SIZE(I30_NAME) || ++ memcmp(attr_name(attr), I30_NAME, sizeof(I30_NAME))) ++ goto next_attr; ++ ++ if (root->type != ATTR_NAME || ++ root->rule != NTFS_COLLATION_TYPE_FILENAME) ++ goto out; ++ ++ if (!is_dir) ++ goto next_attr; ++ ++ ni->ni_flags |= NI_FLAG_DIR; ++ ++ err = indx_init(&ni->dir, sbi, attr, INDEX_MUTEX_I30); ++ if (err) ++ goto out; ++ ++ mode = sb->s_root ++ ? (S_IFDIR | (0777 & sbi->options.fs_dmask_inv)) ++ : (S_IFDIR | 0777); ++ goto next_attr; ++ ++ case ATTR_ALLOC: ++ if (!is_root || attr->name_len != ARRAY_SIZE(I30_NAME) || ++ memcmp(attr_name(attr), I30_NAME, sizeof(I30_NAME))) ++ goto next_attr; ++ ++ inode->i_size = le64_to_cpu(attr->nres.data_size); ++ ni->i_valid = le64_to_cpu(attr->nres.valid_size); ++ inode_set_bytes(inode, le64_to_cpu(attr->nres.alloc_size)); ++ ++ run = &ni->dir.alloc_run; ++ break; ++ ++ case ATTR_BITMAP: ++ if (ino == MFT_REC_MFT) { ++ if (!attr->non_res) ++ goto out; ++#ifndef CONFIG_NTFS3_64BIT_CLUSTER ++ /* 0x20000000 = 2^32 / 8 */ ++ if (le64_to_cpu(attr->nres.alloc_size) >= 0x20000000) ++ goto out; ++#endif ++ run = &sbi->mft.bitmap.run; ++ break; ++ } else if (is_dir && attr->name_len == ARRAY_SIZE(I30_NAME) && ++ !memcmp(attr_name(attr), I30_NAME, ++ sizeof(I30_NAME)) && ++ attr->non_res) { ++ run = &ni->dir.bitmap_run; ++ break; ++ } ++ goto next_attr; ++ ++ case ATTR_REPARSE: ++ if (attr->name_len) ++ goto next_attr; ++ ++ rp_fa = ni_parse_reparse(ni, attr, &rp); ++ switch (rp_fa) { ++ case REPARSE_LINK: ++ if (!attr->non_res) { ++ inode->i_size = rsize; ++ inode_set_bytes(inode, rsize); ++ t32 = asize; ++ } else { ++ inode->i_size = ++ le64_to_cpu(attr->nres.data_size); ++ t32 = le16_to_cpu(attr->nres.run_off); ++ } ++ ++ /* Looks like normal symlink */ ++ ni->i_valid = inode->i_size; ++ ++ /* Clear directory bit */ ++ if (ni->ni_flags & NI_FLAG_DIR) { ++ indx_clear(&ni->dir); ++ memset(&ni->dir, 0, sizeof(ni->dir)); ++ ni->ni_flags &= ~NI_FLAG_DIR; ++ } else { ++ run_close(&ni->file.run); ++ } ++ mode = S_IFLNK | 0777; ++ is_dir = false; ++ if (attr->non_res) { ++ run = &ni->file.run; ++ goto attr_unpack_run; // double break ++ } ++ break; ++ ++ case REPARSE_COMPRESSED: ++ break; ++ ++ case REPARSE_DEDUPLICATED: ++ break; ++ } ++ goto next_attr; ++ ++ case ATTR_EA_INFO: ++ if (!attr->name_len && ++ resident_data_ex(attr, sizeof(struct EA_INFO))) { ++ ni->ni_flags |= NI_FLAG_EA; ++ /* ++ * ntfs_get_wsl_perm updates inode->i_uid, inode->i_gid, inode->i_mode ++ */ ++ inode->i_mode = mode; ++ ntfs_get_wsl_perm(inode); ++ mode = inode->i_mode; ++ } ++ goto next_attr; ++ ++ default: ++ goto next_attr; ++ } ++ ++attr_unpack_run: ++ roff = le16_to_cpu(attr->nres.run_off); ++ ++ t64 = le64_to_cpu(attr->nres.svcn); ++ err = run_unpack_ex(run, sbi, ino, t64, le64_to_cpu(attr->nres.evcn), ++ t64, Add2Ptr(attr, roff), asize - roff); ++ if (err < 0) ++ goto out; ++ err = 0; ++ goto next_attr; ++ ++end_enum: ++ ++ if (!std5) ++ goto out; ++ ++ if (!is_match && name) { ++ /* reuse rec as buffer for ascii name */ ++ err = -ENOENT; ++ goto out; ++ } ++ ++ if (std5->fa & FILE_ATTRIBUTE_READONLY) ++ mode &= ~0222; ++ ++ if (!names) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ set_nlink(inode, names); ++ ++ if (S_ISDIR(mode)) { ++ ni->std_fa |= FILE_ATTRIBUTE_DIRECTORY; ++ ++ /* ++ * dot and dot-dot should be included in count but was not ++ * included in enumeration. ++ * Usually a hard links to directories are disabled ++ */ ++ inode->i_op = &ntfs_dir_inode_operations; ++ inode->i_fop = &ntfs_dir_operations; ++ ni->i_valid = 0; ++ } else if (S_ISLNK(mode)) { ++ ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; ++ inode->i_op = &ntfs_link_inode_operations; ++ inode->i_fop = NULL; ++ inode_nohighmem(inode); // ?? ++ } else if (S_ISREG(mode)) { ++ ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; ++ inode->i_op = &ntfs_file_inode_operations; ++ inode->i_fop = &ntfs_file_operations; ++ inode->i_mapping->a_ops = ++ is_compressed(ni) ? &ntfs_aops_cmpr : &ntfs_aops; ++ if (ino != MFT_REC_MFT) ++ init_rwsem(&ni->file.run_lock); ++ } else if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || ++ S_ISSOCK(mode)) { ++ inode->i_op = &ntfs_special_inode_operations; ++ init_special_inode(inode, mode, inode->i_rdev); ++ } else if (fname && fname->home.low == cpu_to_le32(MFT_REC_EXTEND) && ++ fname->home.seq == cpu_to_le16(MFT_REC_EXTEND)) { ++ /* Records in $Extend are not a files or general directories */ ++ } else { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if ((sbi->options.sys_immutable && ++ (std5->fa & FILE_ATTRIBUTE_SYSTEM)) && ++ !S_ISFIFO(mode) && !S_ISSOCK(mode) && !S_ISLNK(mode)) { ++ inode->i_flags |= S_IMMUTABLE; ++ } else { ++ inode->i_flags &= ~S_IMMUTABLE; ++ } ++ ++ inode->i_mode = mode; ++ if (!(ni->ni_flags & NI_FLAG_EA)) { ++ /* if no xattr then no security (stored in xattr) */ ++ inode->i_flags |= S_NOSEC; ++ } ++ ++Ok: ++ if (ino == MFT_REC_MFT && !sb->s_root) ++ sbi->mft.ni = NULL; ++ ++ unlock_new_inode(inode); ++ ++ return inode; ++ ++out: ++ if (ino == MFT_REC_MFT && !sb->s_root) ++ sbi->mft.ni = NULL; ++ ++ iget_failed(inode); ++ return ERR_PTR(err); ++} ++ ++/* returns 1 if match */ ++static int ntfs_test_inode(struct inode *inode, void *data) ++{ ++ struct MFT_REF *ref = data; ++ ++ return ino_get(ref) == inode->i_ino; ++} ++ ++static int ntfs_set_inode(struct inode *inode, void *data) ++{ ++ const struct MFT_REF *ref = data; ++ ++ inode->i_ino = ino_get(ref); ++ return 0; ++} ++ ++struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, ++ const struct cpu_str *name) ++{ ++ struct inode *inode; ++ ++ inode = iget5_locked(sb, ino_get(ref), ntfs_test_inode, ntfs_set_inode, ++ (void *)ref); ++ if (unlikely(!inode)) ++ return ERR_PTR(-ENOMEM); ++ ++ /* If this is a freshly allocated inode, need to read it now. */ ++ if (inode->i_state & I_NEW) ++ inode = ntfs_read_mft(inode, name, ref); ++ else if (ref->seq != ntfs_i(inode)->mi.mrec->seq) { ++ /* inode overlaps? */ ++ make_bad_inode(inode); ++ } ++ ++ return inode; ++} ++ ++enum get_block_ctx { ++ GET_BLOCK_GENERAL = 0, ++ GET_BLOCK_WRITE_BEGIN = 1, ++ GET_BLOCK_DIRECT_IO_R = 2, ++ GET_BLOCK_DIRECT_IO_W = 3, ++ GET_BLOCK_BMAP = 4, ++}; ++ ++static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, ++ struct buffer_head *bh, int create, ++ enum get_block_ctx ctx) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct page *page = bh->b_page; ++ u8 cluster_bits = sbi->cluster_bits; ++ u32 block_size = sb->s_blocksize; ++ u64 bytes, lbo, valid; ++ u32 off; ++ int err; ++ CLST vcn, lcn, len; ++ bool new; ++ ++ /*clear previous state*/ ++ clear_buffer_new(bh); ++ clear_buffer_uptodate(bh); ++ ++ /* direct write uses 'create=0'*/ ++ if (!create && vbo >= ni->i_valid) { ++ /* out of valid */ ++ return 0; ++ } ++ ++ if (vbo >= inode->i_size) { ++ /* out of size */ ++ return 0; ++ } ++ ++ if (is_resident(ni)) { ++ ni_lock(ni); ++ err = attr_data_read_resident(ni, page); ++ ni_unlock(ni); ++ ++ if (!err) ++ set_buffer_uptodate(bh); ++ bh->b_size = block_size; ++ return err; ++ } ++ ++ vcn = vbo >> cluster_bits; ++ off = vbo & sbi->cluster_mask; ++ new = false; ++ ++ err = attr_data_get_block(ni, vcn, 1, &lcn, &len, create ? &new : NULL); ++ if (err) ++ goto out; ++ ++ if (!len) ++ return 0; ++ ++ bytes = ((u64)len << cluster_bits) - off; ++ ++ if (lcn == SPARSE_LCN) { ++ if (!create) { ++ if (bh->b_size > bytes) ++ bh->b_size = bytes; ++ return 0; ++ } ++ WARN_ON(1); ++ } ++ ++ if (new) { ++ set_buffer_new(bh); ++ if ((len << cluster_bits) > block_size) ++ ntfs_sparse_cluster(inode, page, vcn, len); ++ } ++ ++ lbo = ((u64)lcn << cluster_bits) + off; ++ ++ set_buffer_mapped(bh); ++ bh->b_bdev = sb->s_bdev; ++ bh->b_blocknr = lbo >> sb->s_blocksize_bits; ++ ++ valid = ni->i_valid; ++ ++ if (ctx == GET_BLOCK_DIRECT_IO_W) { ++ /*ntfs_direct_IO will update ni->i_valid */ ++ if (vbo >= valid) ++ set_buffer_new(bh); ++ } else if (create) { ++ /*normal write*/ ++ if (bytes > bh->b_size) ++ bytes = bh->b_size; ++ ++ if (vbo >= valid) ++ set_buffer_new(bh); ++ ++ if (vbo + bytes > valid) { ++ ni->i_valid = vbo + bytes; ++ mark_inode_dirty(inode); ++ } ++ } else if (vbo >= valid) { ++ /* read out of valid data*/ ++ /* should never be here 'cause already checked */ ++ clear_buffer_mapped(bh); ++ } else if (vbo + bytes <= valid) { ++ /* normal read */ ++ } else if (vbo + block_size <= valid) { ++ /* normal short read */ ++ bytes = block_size; ++ } else { ++ /* ++ * read across valid size: vbo < valid && valid < vbo + block_size ++ */ ++ bytes = block_size; ++ ++ if (page) { ++ u32 voff = valid - vbo; ++ ++ bh->b_size = block_size; ++ off = vbo & (PAGE_SIZE - 1); ++ set_bh_page(bh, page, off); ++ ll_rw_block(REQ_OP_READ, 0, 1, &bh); ++ wait_on_buffer(bh); ++ if (!buffer_uptodate(bh)) { ++ err = -EIO; ++ goto out; ++ } ++ zero_user_segment(page, off + voff, off + block_size); ++ } ++ } ++ ++ if (bh->b_size > bytes) ++ bh->b_size = bytes; ++ ++#ifndef __LP64__ ++ if (ctx == GET_BLOCK_DIRECT_IO_W || ctx == GET_BLOCK_DIRECT_IO_R) { ++ static_assert(sizeof(size_t) < sizeof(loff_t)); ++ if (bytes > 0x40000000u) ++ bh->b_size = 0x40000000u; ++ } ++#endif ++ ++ return 0; ++ ++out: ++ return err; ++} ++ ++int ntfs_get_block(struct inode *inode, sector_t vbn, ++ struct buffer_head *bh_result, int create) ++{ ++ return ntfs_get_block_vbo(inode, (u64)vbn << inode->i_blkbits, ++ bh_result, create, GET_BLOCK_GENERAL); ++} ++ ++static int ntfs_get_block_bmap(struct inode *inode, sector_t vsn, ++ struct buffer_head *bh_result, int create) ++{ ++ return ntfs_get_block_vbo(inode, ++ (u64)vsn << inode->i_sb->s_blocksize_bits, ++ bh_result, create, GET_BLOCK_BMAP); ++} ++ ++static sector_t ntfs_bmap(struct address_space *mapping, sector_t block) ++{ ++ return generic_block_bmap(mapping, block, ntfs_get_block_bmap); ++} ++ ++static int ntfs_readpage(struct file *file, struct page *page) ++{ ++ int err; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (is_resident(ni)) { ++ ni_lock(ni); ++ err = attr_data_read_resident(ni, page); ++ ni_unlock(ni); ++ if (err != E_NTFS_NONRESIDENT) { ++ unlock_page(page); ++ return err; ++ } ++ } ++ ++ if (is_compressed(ni)) { ++ ni_lock(ni); ++ err = ni_readpage_cmpr(ni, page); ++ ni_unlock(ni); ++ return err; ++ } ++ ++ /* normal + sparse files */ ++ return mpage_readpage(page, ntfs_get_block); ++} ++ ++static void ntfs_readahead(struct readahead_control *rac) ++{ ++ struct address_space *mapping = rac->mapping; ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ u64 valid; ++ loff_t pos; ++ ++ if (is_resident(ni)) { ++ /* no readahead for resident */ ++ return; ++ } ++ ++ if (is_compressed(ni)) { ++ /* no readahead for compressed */ ++ return; ++ } ++ ++ valid = ni->i_valid; ++ pos = readahead_pos(rac); ++ ++ if (valid < i_size_read(inode) && pos <= valid && ++ valid < pos + readahead_length(rac)) { ++ /* range cross 'valid'. read it page by page */ ++ return; ++ } ++ ++ mpage_readahead(rac, ntfs_get_block); ++} ++ ++static int ntfs_get_block_direct_IO_R(struct inode *inode, sector_t iblock, ++ struct buffer_head *bh_result, int create) ++{ ++ return ntfs_get_block_vbo(inode, (u64)iblock << inode->i_blkbits, ++ bh_result, create, GET_BLOCK_DIRECT_IO_R); ++} ++ ++static int ntfs_get_block_direct_IO_W(struct inode *inode, sector_t iblock, ++ struct buffer_head *bh_result, int create) ++{ ++ return ntfs_get_block_vbo(inode, (u64)iblock << inode->i_blkbits, ++ bh_result, create, GET_BLOCK_DIRECT_IO_W); ++} ++ ++static ssize_t ntfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ++{ ++ struct file *file = iocb->ki_filp; ++ struct address_space *mapping = file->f_mapping; ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ loff_t vbo = iocb->ki_pos; ++ loff_t end; ++ int wr = iov_iter_rw(iter) & WRITE; ++ loff_t valid; ++ ssize_t ret; ++ ++ if (is_resident(ni)) { ++ /*switch to buffered write*/ ++ ret = 0; ++ goto out; ++ } ++ ++ ret = blockdev_direct_IO(iocb, inode, iter, ++ wr ? ntfs_get_block_direct_IO_W ++ : ntfs_get_block_direct_IO_R); ++ ++ if (ret <= 0) ++ goto out; ++ ++ end = vbo + ret; ++ valid = ni->i_valid; ++ if (wr) { ++ if (end > valid && !S_ISBLK(inode->i_mode)) { ++ ni->i_valid = end; ++ mark_inode_dirty(inode); ++ } ++ } else if (vbo < valid && valid < end) { ++ /* fix page */ ++ iov_iter_revert(iter, end - valid); ++ iov_iter_zero(end - valid, iter); ++ } ++ ++out: ++ return ret; ++} ++ ++int ntfs_set_size(struct inode *inode, u64 new_size) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ int err; ++ ++ /* Check for maximum file size */ ++ if (is_sparsed(ni) || is_compressed(ni)) { ++ if (new_size > sbi->maxbytes_sparse) { ++ err = -EFBIG; ++ goto out; ++ } ++ } else if (new_size > sbi->maxbytes) { ++ err = -EFBIG; ++ goto out; ++ } ++ ++ ni_lock(ni); ++ down_write(&ni->file.run_lock); ++ ++ err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size, ++ &ni->i_valid, true, NULL); ++ ++ up_write(&ni->file.run_lock); ++ ni_unlock(ni); ++ ++ mark_inode_dirty(inode); ++ ++out: ++ return err; ++} ++ ++static int ntfs_writepage(struct page *page, struct writeback_control *wbc) ++{ ++ struct address_space *mapping = page->mapping; ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ int err; ++ ++ if (is_resident(ni)) { ++ ni_lock(ni); ++ err = attr_data_write_resident(ni, page); ++ ni_unlock(ni); ++ if (err != E_NTFS_NONRESIDENT) { ++ unlock_page(page); ++ return err; ++ } ++ } ++ ++ return block_write_full_page(page, ntfs_get_block, wbc); ++} ++ ++static int ntfs_writepages(struct address_space *mapping, ++ struct writeback_control *wbc) ++{ ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ /* redirect call to 'ntfs_writepage' for resident files*/ ++ get_block_t *get_block = is_resident(ni) ? NULL : &ntfs_get_block; ++ ++ return mpage_writepages(mapping, wbc, get_block); ++} ++ ++static int ntfs_get_block_write_begin(struct inode *inode, sector_t vbn, ++ struct buffer_head *bh_result, int create) ++{ ++ return ntfs_get_block_vbo(inode, (u64)vbn << inode->i_blkbits, ++ bh_result, create, GET_BLOCK_WRITE_BEGIN); ++} ++ ++static int ntfs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, u32 len, u32 flags, struct page **pagep, ++ void **fsdata) ++{ ++ int err; ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ *pagep = NULL; ++ if (is_resident(ni)) { ++ struct page *page = grab_cache_page_write_begin( ++ mapping, pos >> PAGE_SHIFT, flags); ++ ++ if (!page) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ ni_lock(ni); ++ err = attr_data_read_resident(ni, page); ++ ni_unlock(ni); ++ ++ if (!err) { ++ *pagep = page; ++ goto out; ++ } ++ unlock_page(page); ++ put_page(page); ++ ++ if (err != E_NTFS_NONRESIDENT) ++ goto out; ++ } ++ ++ err = block_write_begin(mapping, pos, len, flags, pagep, ++ ntfs_get_block_write_begin); ++ ++out: ++ return err; ++} ++ ++/* address_space_operations::write_end */ ++static int ntfs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, u32 len, u32 copied, struct page *page, ++ void *fsdata) ++ ++{ ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ u64 valid = ni->i_valid; ++ bool dirty = false; ++ int err; ++ ++ if (is_resident(ni)) { ++ ni_lock(ni); ++ err = attr_data_write_resident(ni, page); ++ ni_unlock(ni); ++ if (!err) { ++ dirty = true; ++ /* clear any buffers in page*/ ++ if (page_has_buffers(page)) { ++ struct buffer_head *head, *bh; ++ ++ bh = head = page_buffers(page); ++ do { ++ clear_buffer_dirty(bh); ++ clear_buffer_mapped(bh); ++ set_buffer_uptodate(bh); ++ } while (head != (bh = bh->b_this_page)); ++ } ++ SetPageUptodate(page); ++ err = copied; ++ } ++ unlock_page(page); ++ put_page(page); ++ } else { ++ err = generic_write_end(file, mapping, pos, len, copied, page, ++ fsdata); ++ } ++ ++ if (err >= 0) { ++ if (!(ni->std_fa & FILE_ATTRIBUTE_ARCHIVE)) { ++ inode->i_ctime = inode->i_mtime = current_time(inode); ++ ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE; ++ dirty = true; ++ } ++ ++ if (valid != ni->i_valid) { ++ /* ni->i_valid is changed in ntfs_get_block_vbo */ ++ dirty = true; ++ } ++ ++ if (dirty) ++ mark_inode_dirty(inode); ++ } ++ ++ return err; ++} ++ ++int reset_log_file(struct inode *inode) ++{ ++ int err; ++ loff_t pos = 0; ++ u32 log_size = inode->i_size; ++ struct address_space *mapping = inode->i_mapping; ++ ++ for (;;) { ++ u32 len; ++ void *kaddr; ++ struct page *page; ++ ++ len = pos + PAGE_SIZE > log_size ? (log_size - pos) : PAGE_SIZE; ++ ++ err = block_write_begin(mapping, pos, len, 0, &page, ++ ntfs_get_block_write_begin); ++ if (err) ++ goto out; ++ ++ kaddr = kmap_atomic(page); ++ memset(kaddr, -1, len); ++ kunmap_atomic(kaddr); ++ flush_dcache_page(page); ++ ++ err = block_write_end(NULL, mapping, pos, len, len, page, NULL); ++ if (err < 0) ++ goto out; ++ pos += len; ++ ++ if (pos >= log_size) ++ break; ++ balance_dirty_pages_ratelimited(mapping); ++ } ++out: ++ mark_inode_dirty_sync(inode); ++ ++ return err; ++} ++ ++int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc) ++{ ++ return _ni_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); ++} ++ ++int ntfs_sync_inode(struct inode *inode) ++{ ++ return _ni_write_inode(inode, 1); ++} ++ ++/* ++ * helper function for ntfs_flush_inodes. This writes both the inode ++ * and the file data blocks, waiting for in flight data blocks before ++ * the start of the call. It does not wait for any io started ++ * during the call ++ */ ++static int writeback_inode(struct inode *inode) ++{ ++ int ret = sync_inode_metadata(inode, 0); ++ ++ if (!ret) ++ ret = filemap_fdatawrite(inode->i_mapping); ++ return ret; ++} ++ ++/* ++ * write data and metadata corresponding to i1 and i2. The io is ++ * started but we do not wait for any of it to finish. ++ * ++ * filemap_flush is used for the block device, so if there is a dirty ++ * page for a block already in flight, we will not wait and start the ++ * io over again ++ */ ++int ntfs_flush_inodes(struct super_block *sb, struct inode *i1, ++ struct inode *i2) ++{ ++ int ret = 0; ++ ++ if (i1) ++ ret = writeback_inode(i1); ++ if (!ret && i2) ++ ret = writeback_inode(i2); ++ if (!ret) ++ ret = filemap_flush(sb->s_bdev->bd_inode->i_mapping); ++ return ret; ++} ++ ++int inode_write_data(struct inode *inode, const void *data, size_t bytes) ++{ ++ pgoff_t idx; ++ ++ /* Write non resident data */ ++ for (idx = 0; bytes; idx++) { ++ size_t op = bytes > PAGE_SIZE ? PAGE_SIZE : bytes; ++ struct page *page = ntfs_map_page(inode->i_mapping, idx); ++ ++ if (IS_ERR(page)) ++ return PTR_ERR(page); ++ ++ lock_page(page); ++ WARN_ON(!PageUptodate(page)); ++ ClearPageUptodate(page); ++ ++ memcpy(page_address(page), data, op); ++ ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ ntfs_unmap_page(page); ++ ++ bytes -= op; ++ data = Add2Ptr(data, PAGE_SIZE); ++ } ++ return 0; ++} ++ ++/* ++ * number of bytes to for REPARSE_DATA_BUFFER(IO_REPARSE_TAG_SYMLINK) ++ * for unicode string of 'uni_len' length ++ */ ++static inline u32 ntfs_reparse_bytes(u32 uni_len) ++{ ++ /* header + unicode string + decorated unicode string */ ++ return sizeof(short) * (2 * uni_len + 4) + ++ offsetof(struct REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer.PathBuffer); ++} ++ ++static struct REPARSE_DATA_BUFFER * ++ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname, ++ u32 size, u16 *nsize) ++{ ++ int i, err; ++ struct REPARSE_DATA_BUFFER *rp; ++ __le16 *rp_name; ++ typeof(rp->SymbolicLinkReparseBuffer) *rs; ++ ++ rp = ntfs_zalloc(ntfs_reparse_bytes(2 * size + 2)); ++ if (!rp) ++ return ERR_PTR(-ENOMEM); ++ ++ rs = &rp->SymbolicLinkReparseBuffer; ++ rp_name = rs->PathBuffer; ++ ++ /* Convert link name to utf16 */ ++ err = ntfs_nls_to_utf16(sbi, symname, size, ++ (struct cpu_str *)(rp_name - 1), 2 * size, ++ UTF16_LITTLE_ENDIAN); ++ if (err < 0) ++ goto out; ++ ++ /* err = the length of unicode name of symlink */ ++ *nsize = ntfs_reparse_bytes(err); ++ ++ if (*nsize > sbi->reparse.max_size) { ++ err = -EFBIG; ++ goto out; ++ } ++ ++ /* translate linux '/' into windows '\' */ ++ for (i = 0; i < err; i++) { ++ if (rp_name[i] == cpu_to_le16('/')) ++ rp_name[i] = cpu_to_le16('\\'); ++ } ++ ++ rp->ReparseTag = IO_REPARSE_TAG_SYMLINK; ++ rp->ReparseDataLength = ++ cpu_to_le16(*nsize - offsetof(struct REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer)); ++ ++ /* PrintName + SubstituteName */ ++ rs->SubstituteNameOffset = cpu_to_le16(sizeof(short) * err); ++ rs->SubstituteNameLength = cpu_to_le16(sizeof(short) * err + 8); ++ rs->PrintNameLength = rs->SubstituteNameOffset; ++ ++ /* ++ * TODO: use relative path if possible to allow windows to parse this path ++ * 0-absolute path 1- relative path (SYMLINK_FLAG_RELATIVE) ++ */ ++ rs->Flags = 0; ++ ++ memmove(rp_name + err + 4, rp_name, sizeof(short) * err); ++ ++ /* decorate SubstituteName */ ++ rp_name += err; ++ rp_name[0] = cpu_to_le16('\\'); ++ rp_name[1] = cpu_to_le16('?'); ++ rp_name[2] = cpu_to_le16('?'); ++ rp_name[3] = cpu_to_le16('\\'); ++ ++ return rp; ++out: ++ ntfs_free(rp); ++ return ERR_PTR(err); ++} ++ ++struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, ++ struct inode *dir, struct dentry *dentry, ++ const struct cpu_str *uni, umode_t mode, ++ dev_t dev, const char *symname, u32 size, ++ struct ntfs_fnd *fnd) ++{ ++ int err; ++ struct super_block *sb = dir->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ const struct qstr *name = &dentry->d_name; ++ CLST ino = 0; ++ struct ntfs_inode *dir_ni = ntfs_i(dir); ++ struct ntfs_inode *ni = NULL; ++ struct inode *inode = NULL; ++ struct ATTRIB *attr; ++ struct ATTR_STD_INFO5 *std5; ++ struct ATTR_FILE_NAME *fname; ++ struct MFT_REC *rec; ++ u32 asize, dsize, sd_size; ++ enum FILE_ATTRIBUTE fa; ++ __le32 security_id = SECURITY_ID_INVALID; ++ CLST vcn; ++ const void *sd; ++ u16 t16, nsize = 0, aid = 0; ++ struct INDEX_ROOT *root, *dir_root; ++ struct NTFS_DE *e, *new_de = NULL; ++ struct REPARSE_DATA_BUFFER *rp = NULL; ++ bool rp_inserted = false; ++ ++ dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL); ++ if (!dir_root) ++ return ERR_PTR(-EINVAL); ++ ++ if (S_ISDIR(mode)) { ++ /* use parent's directory attributes */ ++ fa = dir_ni->std_fa | FILE_ATTRIBUTE_DIRECTORY | ++ FILE_ATTRIBUTE_ARCHIVE; ++ /* ++ * By default child directory inherits parent attributes ++ * root directory is hidden + system ++ * Make an exception for children in root ++ */ ++ if (dir->i_ino == MFT_REC_ROOT) ++ fa &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); ++ } else if (S_ISLNK(mode)) { ++ /* It is good idea that link should be the same type (file/dir) as target */ ++ fa = FILE_ATTRIBUTE_REPARSE_POINT; ++ ++ /* ++ * linux: there are dir/file/symlink and so on ++ * NTFS: symlinks are "dir + reparse" or "file + reparse" ++ * It is good idea to create: ++ * dir + reparse if 'symname' points to directory ++ * or ++ * file + reparse if 'symname' points to file ++ * Unfortunately kern_path hangs if symname contains 'dir' ++ */ ++ ++ /* ++ * struct path path; ++ * ++ * if (!kern_path(symname, LOOKUP_FOLLOW, &path)){ ++ * struct inode *target = d_inode(path.dentry); ++ * ++ * if (S_ISDIR(target->i_mode)) ++ * fa |= FILE_ATTRIBUTE_DIRECTORY; ++ * // if ( target->i_sb == sb ){ ++ * // use relative path? ++ * // } ++ * path_put(&path); ++ * } ++ */ ++ } else if (S_ISREG(mode)) { ++ if (sbi->options.sparse) { ++ /* sparsed regular file, cause option 'sparse' */ ++ fa = FILE_ATTRIBUTE_SPARSE_FILE | ++ FILE_ATTRIBUTE_ARCHIVE; ++ } else if (dir_ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) { ++ /* compressed regular file, if parent is compressed */ ++ fa = FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ARCHIVE; ++ } else { ++ /* regular file, default attributes */ ++ fa = FILE_ATTRIBUTE_ARCHIVE; ++ } ++ } else { ++ fa = FILE_ATTRIBUTE_ARCHIVE; ++ } ++ ++ if (!(mode & 0222)) ++ fa |= FILE_ATTRIBUTE_READONLY; ++ ++ /* allocate PATH_MAX bytes */ ++ new_de = __getname(); ++ if (!new_de) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); ++ ++ /* Step 1: allocate and fill new mft record */ ++ err = ntfs_look_free_mft(sbi, &ino, false, NULL, NULL); ++ if (err) ++ goto out2; ++ ++ ni = ntfs_new_inode(sbi, ino, fa & FILE_ATTRIBUTE_DIRECTORY); ++ if (IS_ERR(ni)) { ++ err = PTR_ERR(ni); ++ ni = NULL; ++ goto out3; ++ } ++ inode = &ni->vfs_inode; ++ inode_init_owner(mnt_userns, inode, dir, mode); ++ ++ inode->i_atime = inode->i_mtime = inode->i_ctime = ni->i_crtime = ++ current_time(inode); ++ ++ rec = ni->mi.mrec; ++ rec->hard_links = cpu_to_le16(1); ++ attr = Add2Ptr(rec, le16_to_cpu(rec->attr_off)); ++ ++ /* Get default security id */ ++ sd = s_default_security; ++ sd_size = sizeof(s_default_security); ++ ++ if (is_ntfs3(sbi)) { ++ security_id = dir_ni->std_security_id; ++ if (le32_to_cpu(security_id) < SECURITY_ID_FIRST) { ++ security_id = sbi->security.def_security_id; ++ ++ if (security_id == SECURITY_ID_INVALID && ++ !ntfs_insert_security(sbi, sd, sd_size, ++ &security_id, NULL)) ++ sbi->security.def_security_id = security_id; ++ } ++ } ++ ++ /* Insert standard info */ ++ std5 = Add2Ptr(attr, SIZEOF_RESIDENT); ++ ++ if (security_id == SECURITY_ID_INVALID) { ++ dsize = sizeof(struct ATTR_STD_INFO); ++ } else { ++ dsize = sizeof(struct ATTR_STD_INFO5); ++ std5->security_id = security_id; ++ ni->std_security_id = security_id; ++ } ++ asize = SIZEOF_RESIDENT + dsize; ++ ++ attr->type = ATTR_STD; ++ attr->size = cpu_to_le32(asize); ++ attr->id = cpu_to_le16(aid++); ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ attr->res.data_size = cpu_to_le32(dsize); ++ ++ std5->cr_time = std5->m_time = std5->c_time = std5->a_time = ++ kernel2nt(&inode->i_atime); ++ ++ ni->std_fa = fa; ++ std5->fa = fa; ++ ++ attr = Add2Ptr(attr, asize); ++ ++ /* Insert file name */ ++ err = fill_name_de(sbi, new_de, name, uni); ++ if (err) ++ goto out4; ++ ++ mi_get_ref(&ni->mi, &new_de->ref); ++ ++ fname = (struct ATTR_FILE_NAME *)(new_de + 1); ++ mi_get_ref(&dir_ni->mi, &fname->home); ++ fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time = ++ fname->dup.a_time = std5->cr_time; ++ fname->dup.alloc_size = fname->dup.data_size = 0; ++ fname->dup.fa = std5->fa; ++ fname->dup.ea_size = fname->dup.reparse = 0; ++ ++ dsize = le16_to_cpu(new_de->key_size); ++ asize = QuadAlign(SIZEOF_RESIDENT + dsize); ++ ++ attr->type = ATTR_NAME; ++ attr->size = cpu_to_le32(asize); ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ attr->res.flags = RESIDENT_FLAG_INDEXED; ++ attr->id = cpu_to_le16(aid++); ++ attr->res.data_size = cpu_to_le32(dsize); ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), fname, dsize); ++ ++ attr = Add2Ptr(attr, asize); ++ ++ if (security_id == SECURITY_ID_INVALID) { ++ /* Insert security attribute */ ++ asize = SIZEOF_RESIDENT + QuadAlign(sd_size); ++ ++ attr->type = ATTR_SECURE; ++ attr->size = cpu_to_le32(asize); ++ attr->id = cpu_to_le16(aid++); ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ attr->res.data_size = cpu_to_le32(sd_size); ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), sd, sd_size); ++ ++ attr = Add2Ptr(attr, asize); ++ } ++ ++ if (fa & FILE_ATTRIBUTE_DIRECTORY) { ++ /* ++ * regular directory or symlink to directory ++ * Create root attribute ++ */ ++ dsize = sizeof(struct INDEX_ROOT) + sizeof(struct NTFS_DE); ++ asize = sizeof(I30_NAME) + SIZEOF_RESIDENT + dsize; ++ ++ attr->type = ATTR_ROOT; ++ attr->size = cpu_to_le32(asize); ++ attr->id = cpu_to_le16(aid++); ++ ++ attr->name_len = ARRAY_SIZE(I30_NAME); ++ attr->name_off = SIZEOF_RESIDENT_LE; ++ attr->res.data_off = ++ cpu_to_le16(sizeof(I30_NAME) + SIZEOF_RESIDENT); ++ attr->res.data_size = cpu_to_le32(dsize); ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), I30_NAME, ++ sizeof(I30_NAME)); ++ ++ root = Add2Ptr(attr, sizeof(I30_NAME) + SIZEOF_RESIDENT); ++ memcpy(root, dir_root, offsetof(struct INDEX_ROOT, ihdr)); ++ root->ihdr.de_off = ++ cpu_to_le32(sizeof(struct INDEX_HDR)); // 0x10 ++ root->ihdr.used = cpu_to_le32(sizeof(struct INDEX_HDR) + ++ sizeof(struct NTFS_DE)); ++ root->ihdr.total = root->ihdr.used; ++ ++ e = Add2Ptr(root, sizeof(struct INDEX_ROOT)); ++ e->size = cpu_to_le16(sizeof(struct NTFS_DE)); ++ e->flags = NTFS_IE_LAST; ++ } else if (S_ISLNK(mode)) { ++ /* ++ * symlink to file ++ * Create empty resident data attribute ++ */ ++ asize = SIZEOF_RESIDENT; ++ ++ /* insert empty ATTR_DATA */ ++ attr->type = ATTR_DATA; ++ attr->size = cpu_to_le32(SIZEOF_RESIDENT); ++ attr->id = cpu_to_le16(aid++); ++ attr->name_off = SIZEOF_RESIDENT_LE; ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ } else { ++ /* ++ * regular file or node ++ */ ++ attr->type = ATTR_DATA; ++ attr->id = cpu_to_le16(aid++); ++ ++ if (S_ISREG(mode)) { ++ /* Create empty non resident data attribute */ ++ attr->non_res = 1; ++ attr->nres.evcn = cpu_to_le64(-1ll); ++ if (fa & FILE_ATTRIBUTE_SPARSE_FILE) { ++ attr->size = ++ cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ attr->flags = ATTR_FLAG_SPARSED; ++ asize = SIZEOF_NONRESIDENT_EX + 8; ++ } else if (fa & FILE_ATTRIBUTE_COMPRESSED) { ++ attr->size = ++ cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ attr->flags = ATTR_FLAG_COMPRESSED; ++ attr->nres.c_unit = COMPRESSION_UNIT; ++ asize = SIZEOF_NONRESIDENT_EX + 8; ++ } else { ++ attr->size = ++ cpu_to_le32(SIZEOF_NONRESIDENT + 8); ++ attr->name_off = SIZEOF_NONRESIDENT_LE; ++ asize = SIZEOF_NONRESIDENT + 8; ++ } ++ attr->nres.run_off = attr->name_off; ++ } else { ++ /* Create empty resident data attribute */ ++ attr->size = cpu_to_le32(SIZEOF_RESIDENT); ++ attr->name_off = SIZEOF_RESIDENT_LE; ++ if (fa & FILE_ATTRIBUTE_SPARSE_FILE) ++ attr->flags = ATTR_FLAG_SPARSED; ++ else if (fa & FILE_ATTRIBUTE_COMPRESSED) ++ attr->flags = ATTR_FLAG_COMPRESSED; ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ asize = SIZEOF_RESIDENT; ++ ni->ni_flags |= NI_FLAG_RESIDENT; ++ } ++ } ++ ++ if (S_ISDIR(mode)) { ++ ni->ni_flags |= NI_FLAG_DIR; ++ err = indx_init(&ni->dir, sbi, attr, INDEX_MUTEX_I30); ++ if (err) ++ goto out4; ++ } else if (S_ISLNK(mode)) { ++ rp = ntfs_create_reparse_buffer(sbi, symname, size, &nsize); ++ ++ if (IS_ERR(rp)) { ++ err = PTR_ERR(rp); ++ rp = NULL; ++ goto out4; ++ } ++ ++ /* ++ * Insert ATTR_REPARSE ++ */ ++ attr = Add2Ptr(attr, asize); ++ attr->type = ATTR_REPARSE; ++ attr->id = cpu_to_le16(aid++); ++ ++ /* resident or non resident? */ ++ asize = QuadAlign(SIZEOF_RESIDENT + nsize); ++ t16 = PtrOffset(rec, attr); ++ ++ if (asize + t16 + 8 > sbi->record_size) { ++ CLST alen; ++ CLST clst = bytes_to_cluster(sbi, nsize); ++ ++ /* bytes per runs */ ++ t16 = sbi->record_size - t16 - SIZEOF_NONRESIDENT; ++ ++ attr->non_res = 1; ++ attr->nres.evcn = cpu_to_le64(clst - 1); ++ attr->name_off = SIZEOF_NONRESIDENT_LE; ++ attr->nres.run_off = attr->name_off; ++ attr->nres.data_size = cpu_to_le64(nsize); ++ attr->nres.valid_size = attr->nres.data_size; ++ attr->nres.alloc_size = ++ cpu_to_le64(ntfs_up_cluster(sbi, nsize)); ++ ++ err = attr_allocate_clusters(sbi, &ni->file.run, 0, 0, ++ clst, NULL, 0, &alen, 0, ++ NULL); ++ if (err) ++ goto out5; ++ ++ err = run_pack(&ni->file.run, 0, clst, ++ Add2Ptr(attr, SIZEOF_NONRESIDENT), t16, ++ &vcn); ++ if (err < 0) ++ goto out5; ++ ++ if (vcn != clst) { ++ err = -EINVAL; ++ goto out5; ++ } ++ ++ asize = SIZEOF_NONRESIDENT + QuadAlign(err); ++ inode->i_size = nsize; ++ } else { ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ attr->res.data_size = cpu_to_le32(nsize); ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), rp, nsize); ++ inode->i_size = nsize; ++ nsize = 0; ++ } ++ ++ attr->size = cpu_to_le32(asize); ++ ++ err = ntfs_insert_reparse(sbi, IO_REPARSE_TAG_SYMLINK, ++ &new_de->ref); ++ if (err) ++ goto out5; ++ ++ rp_inserted = true; ++ } ++ ++ attr = Add2Ptr(attr, asize); ++ attr->type = ATTR_END; ++ ++ rec->used = cpu_to_le32(PtrOffset(rec, attr) + 8); ++ rec->next_attr_id = cpu_to_le16(aid); ++ ++ /* Step 2: Add new name in index */ ++ err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd); ++ if (err) ++ goto out6; ++ ++ /* Update current directory record */ ++ mark_inode_dirty(dir); ++ ++ inode->i_generation = le16_to_cpu(rec->seq); ++ ++ dir->i_mtime = dir->i_ctime = inode->i_atime; ++ ++ if (S_ISDIR(mode)) { ++ if (dir->i_mode & S_ISGID) ++ mode |= S_ISGID; ++ inode->i_op = &ntfs_dir_inode_operations; ++ inode->i_fop = &ntfs_dir_operations; ++ } else if (S_ISLNK(mode)) { ++ inode->i_op = &ntfs_link_inode_operations; ++ inode->i_fop = NULL; ++ inode->i_mapping->a_ops = &ntfs_aops; ++ } else if (S_ISREG(mode)) { ++ inode->i_op = &ntfs_file_inode_operations; ++ inode->i_fop = &ntfs_file_operations; ++ inode->i_mapping->a_ops = ++ is_compressed(ni) ? &ntfs_aops_cmpr : &ntfs_aops; ++ init_rwsem(&ni->file.run_lock); ++ } else { ++ inode->i_op = &ntfs_special_inode_operations; ++ init_special_inode(inode, mode, dev); ++ } ++ ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++ if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) { ++ err = ntfs_init_acl(mnt_userns, inode, dir); ++ if (err) ++ goto out6; ++ } else ++#endif ++ { ++ inode->i_flags |= S_NOSEC; ++ } ++ ++ /* Write non resident data */ ++ if (nsize) { ++ err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rp, nsize); ++ if (err) ++ goto out7; ++ } ++ ++ /* call 'd_instantiate' after inode->i_op is set but before finish_open */ ++ d_instantiate(dentry, inode); ++ ++ ntfs_save_wsl_perm(inode); ++ mark_inode_dirty(inode); ++ mark_inode_dirty(dir); ++ ++ /* normal exit */ ++ goto out2; ++ ++out7: ++ ++ /* undo 'indx_insert_entry' */ ++ indx_delete_entry(&dir_ni->dir, dir_ni, new_de + 1, ++ le16_to_cpu(new_de->key_size), sbi); ++out6: ++ if (rp_inserted) ++ ntfs_remove_reparse(sbi, IO_REPARSE_TAG_SYMLINK, &new_de->ref); ++ ++out5: ++ if (S_ISDIR(mode) || run_is_empty(&ni->file.run)) ++ goto out4; ++ ++ run_deallocate(sbi, &ni->file.run, false); ++ ++out4: ++ clear_rec_inuse(rec); ++ clear_nlink(inode); ++ ni->mi.dirty = false; ++ discard_new_inode(inode); ++out3: ++ ntfs_mark_rec_free(sbi, ino); ++ ++out2: ++ __putname(new_de); ++ ntfs_free(rp); ++ ++out1: ++ if (err) ++ return ERR_PTR(err); ++ ++ unlock_new_inode(inode); ++ ++ return inode; ++} ++ ++int ntfs_link_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ struct inode *dir = d_inode(dentry->d_parent); ++ struct ntfs_inode *dir_ni = ntfs_i(dir); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ const struct qstr *name = &dentry->d_name; ++ struct NTFS_DE *new_de = NULL; ++ struct ATTR_FILE_NAME *fname; ++ struct ATTRIB *attr; ++ u16 key_size; ++ struct INDEX_ROOT *dir_root; ++ ++ dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL); ++ if (!dir_root) ++ return -EINVAL; ++ ++ /* allocate PATH_MAX bytes */ ++ new_de = __getname(); ++ if (!new_de) ++ return -ENOMEM; ++ ++ /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY); ++ ++ // Insert file name ++ err = fill_name_de(sbi, new_de, name, NULL); ++ if (err) ++ goto out; ++ ++ key_size = le16_to_cpu(new_de->key_size); ++ err = ni_insert_resident(ni, key_size, ATTR_NAME, NULL, 0, &attr, NULL); ++ if (err) ++ goto out; ++ ++ mi_get_ref(&ni->mi, &new_de->ref); ++ ++ fname = (struct ATTR_FILE_NAME *)(new_de + 1); ++ mi_get_ref(&dir_ni->mi, &fname->home); ++ fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time = ++ fname->dup.a_time = kernel2nt(&inode->i_ctime); ++ fname->dup.alloc_size = fname->dup.data_size = 0; ++ fname->dup.fa = ni->std_fa; ++ fname->dup.ea_size = fname->dup.reparse = 0; ++ ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), fname, key_size); ++ ++ err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, NULL); ++ if (err) ++ goto out; ++ ++ le16_add_cpu(&ni->mi.mrec->hard_links, 1); ++ ni->mi.dirty = true; ++ ++out: ++ __putname(new_de); ++ return err; ++} ++ ++/* ++ * ntfs_unlink_inode ++ * ++ * inode_operations::unlink ++ * inode_operations::rmdir ++ */ ++int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) ++{ ++ int err; ++ struct super_block *sb = dir->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct inode *inode = d_inode(dentry); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ const struct qstr *name = &dentry->d_name; ++ struct ntfs_inode *dir_ni = ntfs_i(dir); ++ struct ntfs_index *indx = &dir_ni->dir; ++ struct cpu_str *uni = NULL; ++ struct ATTR_FILE_NAME *fname; ++ u8 name_type; ++ struct ATTR_LIST_ENTRY *le; ++ struct MFT_REF ref; ++ bool is_dir = S_ISDIR(inode->i_mode); ++ struct INDEX_ROOT *dir_root; ++ ++ dir_root = indx_get_root(indx, dir_ni, NULL, NULL); ++ if (!dir_root) ++ return -EINVAL; ++ ++ ni_lock(ni); ++ ++ if (is_dir && !dir_is_empty(inode)) { ++ err = -ENOTEMPTY; ++ goto out1; ++ } ++ ++ if (ntfs_is_meta_file(sbi, inode->i_ino)) { ++ err = -EINVAL; ++ goto out1; ++ } ++ ++ /* allocate PATH_MAX bytes */ ++ uni = __getname(); ++ if (!uni) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ /* Convert input string to unicode */ ++ err = ntfs_nls_to_utf16(sbi, name->name, name->len, uni, NTFS_NAME_LEN, ++ UTF16_HOST_ENDIAN); ++ if (err < 0) ++ goto out2; ++ ++ /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); ++ ++ /* find name in record */ ++ mi_get_ref(&dir_ni->mi, &ref); ++ ++ le = NULL; ++ fname = ni_fname_name(ni, uni, &ref, &le); ++ if (!fname) { ++ err = -ENOENT; ++ goto out3; ++ } ++ ++ name_type = paired_name(fname->type); ++ ++ err = indx_delete_entry(indx, dir_ni, fname, fname_full_size(fname), ++ sbi); ++ if (err) ++ goto out3; ++ ++ /* Then remove name from mft */ ++ ni_remove_attr_le(ni, attr_from_name(fname), le); ++ ++ le16_add_cpu(&ni->mi.mrec->hard_links, -1); ++ ni->mi.dirty = true; ++ ++ if (name_type != FILE_NAME_POSIX) { ++ /* Now we should delete name by type */ ++ fname = ni_fname_type(ni, name_type, &le); ++ if (fname) { ++ err = indx_delete_entry(indx, dir_ni, fname, ++ fname_full_size(fname), sbi); ++ if (err) ++ goto out3; ++ ++ ni_remove_attr_le(ni, attr_from_name(fname), le); ++ ++ le16_add_cpu(&ni->mi.mrec->hard_links, -1); ++ } ++ } ++out3: ++ switch (err) { ++ case 0: ++ drop_nlink(inode); ++ case -ENOTEMPTY: ++ case -ENOSPC: ++ case -EROFS: ++ break; ++ default: ++ make_bad_inode(inode); ++ } ++ ++ dir->i_mtime = dir->i_ctime = current_time(dir); ++ mark_inode_dirty(dir); ++ inode->i_ctime = dir->i_ctime; ++ if (inode->i_nlink) ++ mark_inode_dirty(inode); ++ ++out2: ++ __putname(uni); ++out1: ++ ni_unlock(ni); ++ return err; ++} ++ ++void ntfs_evict_inode(struct inode *inode) ++{ ++ truncate_inode_pages_final(&inode->i_data); ++ ++ if (inode->i_nlink) ++ _ni_write_inode(inode, inode_needs_sync(inode)); ++ ++ invalidate_inode_buffers(inode); ++ clear_inode(inode); ++ ++ ni_clear(ntfs_i(inode)); ++} ++ ++static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, ++ int buflen) ++{ ++ int i, err = 0; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ u64 i_size = inode->i_size; ++ u16 nlen = 0; ++ void *to_free = NULL; ++ struct REPARSE_DATA_BUFFER *rp; ++ struct le_str *uni; ++ struct ATTRIB *attr; ++ ++ /* Reparse data present. Try to parse it */ ++ static_assert(!offsetof(struct REPARSE_DATA_BUFFER, ReparseTag)); ++ static_assert(sizeof(u32) == sizeof(rp->ReparseTag)); ++ ++ *buffer = 0; ++ ++ /* Read into temporal buffer */ ++ if (i_size > sbi->reparse.max_size || i_size <= sizeof(u32)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_REPARSE, NULL, 0, NULL, NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (!attr->non_res) { ++ rp = resident_data_ex(attr, i_size); ++ if (!rp) { ++ err = -EINVAL; ++ goto out; ++ } ++ } else { ++ rp = ntfs_malloc(i_size); ++ if (!rp) { ++ err = -ENOMEM; ++ goto out; ++ } ++ to_free = rp; ++ err = ntfs_read_run_nb(sbi, &ni->file.run, 0, rp, i_size, NULL); ++ if (err) ++ goto out; ++ } ++ ++ err = -EINVAL; ++ ++ /* Microsoft Tag */ ++ switch (rp->ReparseTag) { ++ case IO_REPARSE_TAG_MOUNT_POINT: ++ /* Mount points and junctions */ ++ /* Can we use 'Rp->MountPointReparseBuffer.PrintNameLength'? */ ++ if (i_size <= offsetof(struct REPARSE_DATA_BUFFER, ++ MountPointReparseBuffer.PathBuffer)) ++ goto out; ++ uni = Add2Ptr(rp, ++ offsetof(struct REPARSE_DATA_BUFFER, ++ MountPointReparseBuffer.PathBuffer) + ++ le16_to_cpu(rp->MountPointReparseBuffer ++ .PrintNameOffset) - ++ 2); ++ nlen = le16_to_cpu(rp->MountPointReparseBuffer.PrintNameLength); ++ break; ++ ++ case IO_REPARSE_TAG_SYMLINK: ++ /* FolderSymbolicLink */ ++ /* Can we use 'Rp->SymbolicLinkReparseBuffer.PrintNameLength'? */ ++ if (i_size <= offsetof(struct REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer.PathBuffer)) ++ goto out; ++ uni = Add2Ptr(rp, ++ offsetof(struct REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer.PathBuffer) + ++ le16_to_cpu(rp->SymbolicLinkReparseBuffer ++ .PrintNameOffset) - ++ 2); ++ nlen = le16_to_cpu( ++ rp->SymbolicLinkReparseBuffer.PrintNameLength); ++ break; ++ ++ case IO_REPARSE_TAG_CLOUD: ++ case IO_REPARSE_TAG_CLOUD_1: ++ case IO_REPARSE_TAG_CLOUD_2: ++ case IO_REPARSE_TAG_CLOUD_3: ++ case IO_REPARSE_TAG_CLOUD_4: ++ case IO_REPARSE_TAG_CLOUD_5: ++ case IO_REPARSE_TAG_CLOUD_6: ++ case IO_REPARSE_TAG_CLOUD_7: ++ case IO_REPARSE_TAG_CLOUD_8: ++ case IO_REPARSE_TAG_CLOUD_9: ++ case IO_REPARSE_TAG_CLOUD_A: ++ case IO_REPARSE_TAG_CLOUD_B: ++ case IO_REPARSE_TAG_CLOUD_C: ++ case IO_REPARSE_TAG_CLOUD_D: ++ case IO_REPARSE_TAG_CLOUD_E: ++ case IO_REPARSE_TAG_CLOUD_F: ++ err = sizeof("OneDrive") - 1; ++ if (err > buflen) ++ err = buflen; ++ memcpy(buffer, "OneDrive", err); ++ goto out; ++ ++ default: ++ if (IsReparseTagMicrosoft(rp->ReparseTag)) { ++ /* unknown Microsoft Tag */ ++ goto out; ++ } ++ if (!IsReparseTagNameSurrogate(rp->ReparseTag) || ++ i_size <= sizeof(struct REPARSE_POINT)) { ++ goto out; ++ } ++ ++ /* Users tag */ ++ uni = Add2Ptr(rp, sizeof(struct REPARSE_POINT) - 2); ++ nlen = le16_to_cpu(rp->ReparseDataLength) - ++ sizeof(struct REPARSE_POINT); ++ } ++ ++ /* Convert nlen from bytes to UNICODE chars */ ++ nlen >>= 1; ++ ++ /* Check that name is available */ ++ if (!nlen || &uni->name[nlen] > (__le16 *)Add2Ptr(rp, i_size)) ++ goto out; ++ ++ /* If name is already zero terminated then truncate it now */ ++ if (!uni->name[nlen - 1]) ++ nlen -= 1; ++ uni->len = nlen; ++ ++ err = ntfs_utf16_to_nls(sbi, uni, buffer, buflen); ++ ++ if (err < 0) ++ goto out; ++ ++ /* translate windows '\' into linux '/' */ ++ for (i = 0; i < err; i++) { ++ if (buffer[i] == '\\') ++ buffer[i] = '/'; ++ } ++ ++ /* Always set last zero */ ++ buffer[err] = 0; ++out: ++ ntfs_free(to_free); ++ return err; ++} ++ ++static const char *ntfs_get_link(struct dentry *de, struct inode *inode, ++ struct delayed_call *done) ++{ ++ int err; ++ char *ret; ++ ++ if (!de) ++ return ERR_PTR(-ECHILD); ++ ++ ret = kmalloc(PAGE_SIZE, GFP_NOFS); ++ if (!ret) ++ return ERR_PTR(-ENOMEM); ++ ++ err = ntfs_readlink_hlp(inode, ret, PAGE_SIZE); ++ if (err < 0) { ++ kfree(ret); ++ return ERR_PTR(err); ++ } ++ ++ set_delayed_call(done, kfree_link, ret); ++ ++ return ret; ++} ++ ++// clang-format off ++const struct inode_operations ntfs_link_inode_operations = { ++ .get_link = ntfs_get_link, ++ .setattr = ntfs3_setattr, ++ .listxattr = ntfs_listxattr, ++ .permission = ntfs_permission, ++ .get_acl = ntfs_get_acl, ++ .set_acl = ntfs_set_acl, ++}; ++ ++const struct address_space_operations ntfs_aops = { ++ .readpage = ntfs_readpage, ++ .readahead = ntfs_readahead, ++ .writepage = ntfs_writepage, ++ .writepages = ntfs_writepages, ++ .write_begin = ntfs_write_begin, ++ .write_end = ntfs_write_end, ++ .direct_IO = ntfs_direct_IO, ++ .bmap = ntfs_bmap, ++ .set_page_dirty = __set_page_dirty_buffers, ++}; ++ ++const struct address_space_operations ntfs_aops_cmpr = { ++ .readpage = ntfs_readpage, ++ .readahead = ntfs_readahead, ++}; ++// clang-format on +diff --git a/src/super.c b/src/super.c +new file mode 100644 +index 0000000000000000000000000000000000000000..6be13e256c1ad18d21ed5727cf8a1129840e774e +--- /dev/null ++++ b/src/super.c +@@ -0,0 +1,1504 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * ++ * terminology ++ * ++ * cluster - allocation unit - 512,1K,2K,4K,...,2M ++ * vcn - virtual cluster number - offset inside the file in clusters ++ * vbo - virtual byte offset - offset inside the file in bytes ++ * lcn - logical cluster number - 0 based cluster in clusters heap ++ * lbo - logical byte offset - absolute position inside volume ++ * run - maps vcn to lcn - stored in attributes in packed form ++ * attr - attribute segment - std/name/data etc records inside MFT ++ * mi - mft inode - one MFT record(usually 1024 bytes or 4K), consists of attributes ++ * ni - ntfs inode - extends linux inode. consists of one or more mft inodes ++ * index - unit inside directory - 2K, 4K, <=page size, does not depend on cluster size ++ * ++ * WSL - Windows Subsystem for Linux ++ * https://docs.microsoft.com/en-us/windows/wsl/file-permissions ++ * It stores uid/gid/mode/dev in xattr ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++#include "lib/lib.h" ++#endif ++ ++#ifdef CONFIG_PRINTK ++/* ++ * Trace warnings/notices/errors ++ * Thanks Joe Perches for implementation ++ */ ++void ntfs_printk(const struct super_block *sb, const char *fmt, ...) ++{ ++ struct va_format vaf; ++ va_list args; ++ int level; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ ++ /*should we use different ratelimits for warnings/notices/errors? */ ++ if (!___ratelimit(&sbi->msg_ratelimit, "ntfs3")) ++ return; ++ ++ va_start(args, fmt); ++ ++ level = printk_get_level(fmt); ++ vaf.fmt = printk_skip_level(fmt); ++ vaf.va = &args; ++ printk("%c%cntfs3: %s: %pV\n", KERN_SOH_ASCII, level, sb->s_id, &vaf); ++ ++ va_end(args); ++} ++ ++static char s_name_buf[512]; ++static atomic_t s_name_buf_cnt = ATOMIC_INIT(1); // 1 means 'free s_name_buf' ++ ++/* print warnings/notices/errors about inode using name or inode number */ ++void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ char *name; ++ va_list args; ++ struct va_format vaf; ++ int level; ++ ++ if (!___ratelimit(&sbi->msg_ratelimit, "ntfs3")) ++ return; ++ ++ /* use static allocated buffer, if possible */ ++ name = atomic_dec_and_test(&s_name_buf_cnt) ++ ? s_name_buf ++ : kmalloc(sizeof(s_name_buf), GFP_NOFS); ++ ++ if (name) { ++ struct dentry *de = d_find_alias(inode); ++ const u32 name_len = ARRAY_SIZE(s_name_buf) - 1; ++ ++ if (de) { ++ spin_lock(&de->d_lock); ++ snprintf(name, name_len, " \"%s\"", de->d_name.name); ++ spin_unlock(&de->d_lock); ++ name[name_len] = 0; /* to be sure*/ ++ } else { ++ name[0] = 0; ++ } ++ dput(de); /* cocci warns if placed in branch "if (de)" */ ++ } ++ ++ va_start(args, fmt); ++ ++ level = printk_get_level(fmt); ++ vaf.fmt = printk_skip_level(fmt); ++ vaf.va = &args; ++ ++ printk("%c%cntfs3: %s: ino=%lx,%s %pV\n", KERN_SOH_ASCII, level, ++ sb->s_id, inode->i_ino, name ? name : "", &vaf); ++ ++ va_end(args); ++ ++ atomic_inc(&s_name_buf_cnt); ++ if (name != s_name_buf) ++ kfree(name); ++} ++#endif ++ ++/* ++ * Shared memory struct. ++ * ++ * on-disk ntfs's upcase table is created by ntfs formater ++ * 'upcase' table is 128K bytes of memory ++ * we should read it into memory when mounting ++ * Several ntfs volumes likely use the same 'upcase' table ++ * It is good idea to share in-memory 'upcase' table between different volumes ++ * Unfortunately winxp/vista/win7 use different upcase tables ++ */ ++static DEFINE_SPINLOCK(s_shared_lock); ++ ++static struct { ++ void *ptr; ++ u32 len; ++ int cnt; ++} s_shared[8]; ++ ++/* ++ * ntfs_set_shared ++ * ++ * Returns 'ptr' if pointer was saved in shared memory ++ * Returns NULL if pointer was not shared ++ */ ++void *ntfs_set_shared(void *ptr, u32 bytes) ++{ ++ void *ret = NULL; ++ int i, j = -1; ++ ++ spin_lock(&s_shared_lock); ++ for (i = 0; i < ARRAY_SIZE(s_shared); i++) { ++ if (!s_shared[i].cnt) { ++ j = i; ++ } else if (bytes == s_shared[i].len && ++ !memcmp(s_shared[i].ptr, ptr, bytes)) { ++ s_shared[i].cnt += 1; ++ ret = s_shared[i].ptr; ++ break; ++ } ++ } ++ ++ if (!ret && j != -1) { ++ s_shared[j].ptr = ptr; ++ s_shared[j].len = bytes; ++ s_shared[j].cnt = 1; ++ ret = ptr; ++ } ++ spin_unlock(&s_shared_lock); ++ ++ return ret; ++} ++ ++/* ++ * ntfs_put_shared ++ * ++ * Returns 'ptr' if pointer is not shared anymore ++ * Returns NULL if pointer is still shared ++ */ ++void *ntfs_put_shared(void *ptr) ++{ ++ void *ret = ptr; ++ int i; ++ ++ spin_lock(&s_shared_lock); ++ for (i = 0; i < ARRAY_SIZE(s_shared); i++) { ++ if (s_shared[i].cnt && s_shared[i].ptr == ptr) { ++ if (--s_shared[i].cnt) ++ ret = NULL; ++ break; ++ } ++ } ++ spin_unlock(&s_shared_lock); ++ ++ return ret; ++} ++ ++static inline void clear_mount_options(struct ntfs_mount_options *options) ++{ ++ unload_nls(options->nls); ++} ++ ++enum Opt { ++ Opt_uid, ++ Opt_gid, ++ Opt_umask, ++ Opt_dmask, ++ Opt_fmask, ++ Opt_immutable, ++ Opt_discard, ++ Opt_force, ++ Opt_sparse, ++ Opt_nohidden, ++ Opt_showmeta, ++ Opt_acl, ++ Opt_noatime, ++ Opt_nls, ++ Opt_prealloc, ++ Opt_no_acs_rules, ++ Opt_err, ++}; ++ ++static const match_table_t ntfs_tokens = { ++ { Opt_uid, "uid=%u" }, ++ { Opt_gid, "gid=%u" }, ++ { Opt_umask, "umask=%o" }, ++ { Opt_dmask, "dmask=%o" }, ++ { Opt_fmask, "fmask=%o" }, ++ { Opt_immutable, "sys_immutable" }, ++ { Opt_discard, "discard" }, ++ { Opt_force, "force" }, ++ { Opt_sparse, "sparse" }, ++ { Opt_nohidden, "nohidden" }, ++ { Opt_acl, "acl" }, ++ { Opt_noatime, "noatime" }, ++ { Opt_showmeta, "showmeta" }, ++ { Opt_nls, "nls=%s" }, ++ { Opt_prealloc, "prealloc" }, ++ { Opt_no_acs_rules, "no_acs_rules" }, ++ { Opt_err, NULL }, ++}; ++ ++static noinline int ntfs_parse_options(struct super_block *sb, char *options, ++ int silent, ++ struct ntfs_mount_options *opts) ++{ ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ int option; ++ char nls_name[30]; ++ struct nls_table *nls; ++ ++ opts->fs_uid = current_uid(); ++ opts->fs_gid = current_gid(); ++ opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask(); ++ nls_name[0] = 0; ++ ++ if (!options) ++ goto out; ++ ++ while ((p = strsep(&options, ","))) { ++ int token; ++ ++ if (!*p) ++ continue; ++ ++ token = match_token(p, ntfs_tokens, args); ++ switch (token) { ++ case Opt_immutable: ++ opts->sys_immutable = 1; ++ break; ++ case Opt_uid: ++ if (match_int(&args[0], &option)) ++ return -EINVAL; ++ opts->fs_uid = make_kuid(current_user_ns(), option); ++ if (!uid_valid(opts->fs_uid)) ++ return -EINVAL; ++ opts->uid = 1; ++ break; ++ case Opt_gid: ++ if (match_int(&args[0], &option)) ++ return -EINVAL; ++ opts->fs_gid = make_kgid(current_user_ns(), option); ++ if (!gid_valid(opts->fs_gid)) ++ return -EINVAL; ++ opts->gid = 1; ++ break; ++ case Opt_umask: ++ if (match_octal(&args[0], &option)) ++ return -EINVAL; ++ opts->fs_fmask_inv = opts->fs_dmask_inv = ~option; ++ opts->fmask = opts->dmask = 1; ++ break; ++ case Opt_dmask: ++ if (match_octal(&args[0], &option)) ++ return -EINVAL; ++ opts->fs_dmask_inv = ~option; ++ opts->dmask = 1; ++ break; ++ case Opt_fmask: ++ if (match_octal(&args[0], &option)) ++ return -EINVAL; ++ opts->fs_fmask_inv = ~option; ++ opts->fmask = 1; ++ break; ++ case Opt_discard: ++ opts->discard = 1; ++ break; ++ case Opt_force: ++ opts->force = 1; ++ break; ++ case Opt_sparse: ++ opts->sparse = 1; ++ break; ++ case Opt_nohidden: ++ opts->nohidden = 1; ++ break; ++ case Opt_acl: ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++ sb->s_flags |= SB_POSIXACL; ++ break; ++#else ++ ntfs_err(sb, "support for ACL not compiled in!"); ++ return -EINVAL; ++#endif ++ case Opt_noatime: ++ sb->s_flags |= SB_NOATIME; ++ break; ++ case Opt_showmeta: ++ opts->showmeta = 1; ++ break; ++ case Opt_nls: ++ match_strlcpy(nls_name, &args[0], sizeof(nls_name)); ++ break; ++ case Opt_prealloc: ++ opts->prealloc = 1; ++ break; ++ case Opt_no_acs_rules: ++ opts->no_acs_rules = 1; ++ break; ++ default: ++ if (!silent) ++ ntfs_err( ++ sb, ++ "Unrecognized mount option \"%s\" or missing value", ++ p); ++ //return -EINVAL; ++ } ++ } ++ ++out: ++ if (!strcmp(nls_name[0] ? nls_name : CONFIG_NLS_DEFAULT, "utf8")) { ++ /* For UTF-8 use utf16s_to_utf8s/utf8s_to_utf16s instead of nls */ ++ nls = NULL; ++ } else if (nls_name[0]) { ++ nls = load_nls(nls_name); ++ if (!nls) { ++ ntfs_err(sb, "failed to load \"%s\"", nls_name); ++ return -EINVAL; ++ } ++ } else { ++ nls = load_nls_default(); ++ if (!nls) { ++ ntfs_err(sb, "failed to load default nls"); ++ return -EINVAL; ++ } ++ } ++ opts->nls = nls; ++ ++ return 0; ++} ++ ++static int ntfs_remount(struct super_block *sb, int *flags, char *data) ++{ ++ int err, ro_rw; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_mount_options old_opts; ++ char *orig_data = kstrdup(data, GFP_KERNEL); ++ ++ if (data && !orig_data) ++ return -ENOMEM; ++ ++ /* Store original options */ ++ memcpy(&old_opts, &sbi->options, sizeof(old_opts)); ++ clear_mount_options(&sbi->options); ++ memset(&sbi->options, 0, sizeof(sbi->options)); ++ ++ err = ntfs_parse_options(sb, data, 0, &sbi->options); ++ if (err) ++ goto restore_opts; ++ ++ ro_rw = sb_rdonly(sb) && !(*flags & SB_RDONLY); ++ if (ro_rw && (sbi->flags & NTFS_FLAGS_NEED_REPLAY)) { ++ ntfs_warn( ++ sb, ++ "Couldn't remount rw because journal is not replayed. Please umount/remount instead\n"); ++ err = -EINVAL; ++ goto restore_opts; ++ } ++ ++ sync_filesystem(sb); ++ ++ if (ro_rw && (sbi->volume.flags & VOLUME_FLAG_DIRTY) && ++ !sbi->options.force) { ++ ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!"); ++ err = -EINVAL; ++ goto restore_opts; ++ } ++ ++ clear_mount_options(&old_opts); ++ ++ *flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME) | ++ SB_NODIRATIME | SB_NOATIME; ++ ntfs_info(sb, "re-mounted. Opts: %s", orig_data); ++ err = 0; ++ goto out; ++ ++restore_opts: ++ clear_mount_options(&sbi->options); ++ memcpy(&sbi->options, &old_opts, sizeof(old_opts)); ++ ++out: ++ kfree(orig_data); ++ return err; ++} ++ ++static struct kmem_cache *ntfs_inode_cachep; ++ ++static struct inode *ntfs_alloc_inode(struct super_block *sb) ++{ ++ struct ntfs_inode *ni = kmem_cache_alloc(ntfs_inode_cachep, GFP_NOFS); ++ ++ if (!ni) ++ return NULL; ++ ++ memset(ni, 0, offsetof(struct ntfs_inode, vfs_inode)); ++ ++ mutex_init(&ni->ni_lock); ++ ++ return &ni->vfs_inode; ++} ++ ++static void ntfs_i_callback(struct rcu_head *head) ++{ ++ struct inode *inode = container_of(head, struct inode, i_rcu); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ mutex_destroy(&ni->ni_lock); ++ ++ kmem_cache_free(ntfs_inode_cachep, ni); ++} ++ ++static void ntfs_destroy_inode(struct inode *inode) ++{ ++ call_rcu(&inode->i_rcu, ntfs_i_callback); ++} ++ ++static void init_once(void *foo) ++{ ++ struct ntfs_inode *ni = foo; ++ ++ inode_init_once(&ni->vfs_inode); ++} ++ ++/* noinline to reduce binary size*/ ++static noinline void put_ntfs(struct ntfs_sb_info *sbi) ++{ ++ ntfs_free(sbi->new_rec); ++ ntfs_vfree(ntfs_put_shared(sbi->upcase)); ++ ntfs_free(sbi->def_table); ++ ++ wnd_close(&sbi->mft.bitmap); ++ wnd_close(&sbi->used.bitmap); ++ ++ if (sbi->mft.ni) ++ iput(&sbi->mft.ni->vfs_inode); ++ ++ if (sbi->security.ni) ++ iput(&sbi->security.ni->vfs_inode); ++ ++ if (sbi->reparse.ni) ++ iput(&sbi->reparse.ni->vfs_inode); ++ ++ if (sbi->objid.ni) ++ iput(&sbi->objid.ni->vfs_inode); ++ ++ if (sbi->volume.ni) ++ iput(&sbi->volume.ni->vfs_inode); ++ ++ ntfs_update_mftmirr(sbi, 0); ++ ++ indx_clear(&sbi->security.index_sii); ++ indx_clear(&sbi->security.index_sdh); ++ indx_clear(&sbi->reparse.index_r); ++ indx_clear(&sbi->objid.index_o); ++ ntfs_free(sbi->compress.lznt); ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ xpress_free_decompressor(sbi->compress.xpress); ++ lzx_free_decompressor(sbi->compress.lzx); ++#endif ++ clear_mount_options(&sbi->options); ++ ++ ntfs_free(sbi); ++} ++ ++static void ntfs_put_super(struct super_block *sb) ++{ ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ ++ /*mark rw ntfs as clear, if possible*/ ++ ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); ++ ++ put_ntfs(sbi); ++ ++ sync_blockdev(sb->s_bdev); ++} ++ ++static int ntfs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct super_block *sb = dentry->d_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct wnd_bitmap *wnd = &sbi->used.bitmap; ++ ++ buf->f_type = sb->s_magic; ++ buf->f_bsize = sbi->cluster_size; ++ buf->f_blocks = wnd->nbits; ++ ++ buf->f_bfree = buf->f_bavail = wnd_zeroes(wnd); ++ buf->f_fsid.val[0] = sbi->volume.ser_num; ++ buf->f_fsid.val[1] = (sbi->volume.ser_num >> 32); ++ buf->f_namelen = NTFS_NAME_LEN; ++ ++ return 0; ++} ++ ++static int ntfs_show_options(struct seq_file *m, struct dentry *root) ++{ ++ struct super_block *sb = root->d_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_mount_options *opts = &sbi->options; ++ struct user_namespace *user_ns = seq_user_ns(m); ++ ++ if (opts->uid) ++ seq_printf(m, ",uid=%u", ++ from_kuid_munged(user_ns, opts->fs_uid)); ++ if (opts->gid) ++ seq_printf(m, ",gid=%u", ++ from_kgid_munged(user_ns, opts->fs_gid)); ++ if (opts->fmask) ++ seq_printf(m, ",fmask=%04o", ~opts->fs_fmask_inv); ++ if (opts->dmask) ++ seq_printf(m, ",dmask=%04o", ~opts->fs_dmask_inv); ++ if (opts->nls) ++ seq_printf(m, ",nls=%s", opts->nls->charset); ++ else ++ seq_puts(m, ",nls=utf8"); ++ if (opts->sys_immutable) ++ seq_puts(m, ",sys_immutable"); ++ if (opts->discard) ++ seq_puts(m, ",discard"); ++ if (opts->sparse) ++ seq_puts(m, ",sparse"); ++ if (opts->showmeta) ++ seq_puts(m, ",showmeta"); ++ if (opts->nohidden) ++ seq_puts(m, ",nohidden"); ++ if (opts->force) ++ seq_puts(m, ",force"); ++ if (opts->no_acs_rules) ++ seq_puts(m, ",no_acs_rules"); ++ if (opts->prealloc) ++ seq_puts(m, ",prealloc"); ++ if (sb->s_flags & SB_POSIXACL) ++ seq_puts(m, ",acl"); ++ if (sb->s_flags & SB_NOATIME) ++ seq_puts(m, ",noatime"); ++ ++ return 0; ++} ++ ++/*super_operations::sync_fs*/ ++static int ntfs_sync_fs(struct super_block *sb, int wait) ++{ ++ int err = 0, err2; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_inode *ni; ++ struct inode *inode; ++ ++ ni = sbi->security.ni; ++ if (ni) { ++ inode = &ni->vfs_inode; ++ err2 = _ni_write_inode(inode, wait); ++ if (err2 && !err) ++ err = err2; ++ } ++ ++ ni = sbi->objid.ni; ++ if (ni) { ++ inode = &ni->vfs_inode; ++ err2 = _ni_write_inode(inode, wait); ++ if (err2 && !err) ++ err = err2; ++ } ++ ++ ni = sbi->reparse.ni; ++ if (ni) { ++ inode = &ni->vfs_inode; ++ err2 = _ni_write_inode(inode, wait); ++ if (err2 && !err) ++ err = err2; ++ } ++ ++ if (!err) ++ ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); ++ ++ ntfs_update_mftmirr(sbi, wait); ++ ++ return err; ++} ++ ++static const struct super_operations ntfs_sops = { ++ .alloc_inode = ntfs_alloc_inode, ++ .destroy_inode = ntfs_destroy_inode, ++ .evict_inode = ntfs_evict_inode, ++ .put_super = ntfs_put_super, ++ .statfs = ntfs_statfs, ++ .show_options = ntfs_show_options, ++ .sync_fs = ntfs_sync_fs, ++ .remount_fs = ntfs_remount, ++ .write_inode = ntfs3_write_inode, ++}; ++ ++static struct inode *ntfs_export_get_inode(struct super_block *sb, u64 ino, ++ u32 generation) ++{ ++ struct MFT_REF ref; ++ struct inode *inode; ++ ++ ref.low = cpu_to_le32(ino); ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++ ref.high = cpu_to_le16(ino >> 32); ++#else ++ ref.high = 0; ++#endif ++ ref.seq = cpu_to_le16(generation); ++ ++ inode = ntfs_iget5(sb, &ref, NULL); ++ if (!IS_ERR(inode) && is_bad_inode(inode)) { ++ iput(inode); ++ inode = ERR_PTR(-ESTALE); ++ } ++ ++ return inode; ++} ++ ++static struct dentry *ntfs_fh_to_dentry(struct super_block *sb, struct fid *fid, ++ int fh_len, int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ ntfs_export_get_inode); ++} ++ ++static struct dentry *ntfs_fh_to_parent(struct super_block *sb, struct fid *fid, ++ int fh_len, int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ ntfs_export_get_inode); ++} ++ ++/* TODO: == ntfs_sync_inode */ ++static int ntfs_nfs_commit_metadata(struct inode *inode) ++{ ++ return _ni_write_inode(inode, 1); ++} ++ ++static const struct export_operations ntfs_export_ops = { ++ .fh_to_dentry = ntfs_fh_to_dentry, ++ .fh_to_parent = ntfs_fh_to_parent, ++ .get_parent = ntfs3_get_parent, ++ .commit_metadata = ntfs_nfs_commit_metadata, ++}; ++ ++/* Returns Gb,Mb to print with "%u.%02u Gb" */ ++static u32 format_size_gb(const u64 bytes, u32 *mb) ++{ ++ /* Do simple right 30 bit shift of 64 bit value */ ++ u64 kbytes = bytes >> 10; ++ u32 kbytes32 = kbytes; ++ ++ *mb = (100 * (kbytes32 & 0xfffff) + 0x7ffff) >> 20; ++ if (*mb >= 100) ++ *mb = 99; ++ ++ return (kbytes32 >> 20) | (((u32)(kbytes >> 32)) << 12); ++} ++ ++static u32 true_sectors_per_clst(const struct NTFS_BOOT *boot) ++{ ++ return boot->sectors_per_clusters <= 0x80 ++ ? boot->sectors_per_clusters ++ : (1u << (0 - boot->sectors_per_clusters)); ++} ++ ++/* inits internal info from on-disk boot sector*/ ++static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, ++ u64 dev_size) ++{ ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ int err; ++ u32 mb, gb, boot_sector_size, sct_per_clst, record_size; ++ u64 sectors, clusters, fs_size, mlcn, mlcn2; ++ struct NTFS_BOOT *boot; ++ struct buffer_head *bh; ++ struct MFT_REC *rec; ++ u16 fn, ao; ++ ++ sbi->volume.blocks = dev_size >> PAGE_SHIFT; ++ ++ bh = ntfs_bread(sb, 0); ++ if (!bh) ++ return -EIO; ++ ++ err = -EINVAL; ++ boot = (struct NTFS_BOOT *)bh->b_data; ++ ++ if (memcmp(boot->system_id, "NTFS ", sizeof("NTFS ") - 1)) ++ goto out; ++ ++ /* 0x55AA is not mandaroty. Thanks Maxim Suhanov*/ ++ /*if (0x55 != boot->boot_magic[0] || 0xAA != boot->boot_magic[1]) ++ * goto out; ++ */ ++ ++ boot_sector_size = (u32)boot->bytes_per_sector[1] << 8; ++ if (boot->bytes_per_sector[0] || boot_sector_size < SECTOR_SIZE || ++ !is_power_of2(boot_sector_size)) { ++ goto out; ++ } ++ ++ /* cluster size: 512, 1K, 2K, 4K, ... 2M */ ++ sct_per_clst = true_sectors_per_clst(boot); ++ if (!is_power_of2(sct_per_clst)) ++ goto out; ++ ++ mlcn = le64_to_cpu(boot->mft_clst); ++ mlcn2 = le64_to_cpu(boot->mft2_clst); ++ sectors = le64_to_cpu(boot->sectors_per_volume); ++ ++ if (mlcn * sct_per_clst >= sectors) ++ goto out; ++ ++ if (mlcn2 * sct_per_clst >= sectors) ++ goto out; ++ ++ /* Check MFT record size */ ++ if ((boot->record_size < 0 && ++ SECTOR_SIZE > (2U << (-boot->record_size))) || ++ (boot->record_size >= 0 && !is_power_of2(boot->record_size))) { ++ goto out; ++ } ++ ++ /* Check index record size */ ++ if ((boot->index_size < 0 && ++ SECTOR_SIZE > (2U << (-boot->index_size))) || ++ (boot->index_size >= 0 && !is_power_of2(boot->index_size))) { ++ goto out; ++ } ++ ++ sbi->sector_size = boot_sector_size; ++ sbi->sector_bits = blksize_bits(boot_sector_size); ++ fs_size = (sectors + 1) << sbi->sector_bits; ++ ++ gb = format_size_gb(fs_size, &mb); ++ ++ /* ++ * - Volume formatted and mounted with the same sector size ++ * - Volume formatted 4K and mounted as 512 ++ * - Volume formatted 512 and mounted as 4K ++ */ ++ if (sbi->sector_size != sector_size) { ++ ntfs_warn(sb, ++ "Different NTFS' sector size and media sector size"); ++ dev_size += sector_size - 1; ++ } ++ ++ sbi->cluster_size = boot_sector_size * sct_per_clst; ++ sbi->cluster_bits = blksize_bits(sbi->cluster_size); ++ ++ sbi->mft.lbo = mlcn << sbi->cluster_bits; ++ sbi->mft.lbo2 = mlcn2 << sbi->cluster_bits; ++ ++ if (sbi->cluster_size < sbi->sector_size) ++ goto out; ++ ++ sbi->cluster_mask = sbi->cluster_size - 1; ++ sbi->cluster_mask_inv = ~(u64)sbi->cluster_mask; ++ sbi->record_size = record_size = boot->record_size < 0 ++ ? 1 << (-boot->record_size) ++ : (u32)boot->record_size ++ << sbi->cluster_bits; ++ ++ if (record_size > MAXIMUM_BYTES_PER_MFT) ++ goto out; ++ ++ sbi->record_bits = blksize_bits(record_size); ++ sbi->attr_size_tr = (5 * record_size >> 4); // ~320 bytes ++ ++ sbi->max_bytes_per_attr = ++ record_size - QuadAlign(MFTRECORD_FIXUP_OFFSET_1) - ++ QuadAlign(((record_size >> SECTOR_SHIFT) * sizeof(short))) - ++ QuadAlign(sizeof(enum ATTR_TYPE)); ++ ++ sbi->index_size = boot->index_size < 0 ++ ? 1u << (-boot->index_size) ++ : (u32)boot->index_size << sbi->cluster_bits; ++ ++ sbi->volume.ser_num = le64_to_cpu(boot->serial_num); ++ sbi->volume.size = sectors << sbi->sector_bits; ++ ++ /* warning if RAW volume */ ++ if (dev_size < fs_size) { ++ u32 mb0, gb0; ++ ++ gb0 = format_size_gb(dev_size, &mb0); ++ ntfs_warn( ++ sb, ++ "RAW NTFS volume: Filesystem size %u.%02u Gb > volume size %u.%02u Gb. Mount in read-only", ++ gb, mb, gb0, mb0); ++ sb->s_flags |= SB_RDONLY; ++ } ++ ++ clusters = sbi->volume.size >> sbi->cluster_bits; ++#ifndef CONFIG_NTFS3_64BIT_CLUSTER ++ /* 32 bits per cluster */ ++ if (clusters >> 32) { ++ ntfs_notice( ++ sb, ++ "NTFS %u.%02u Gb is too big to use 32 bits per cluster", ++ gb, mb); ++ goto out; ++ } ++#elif BITS_PER_LONG < 64 ++#error "CONFIG_NTFS3_64BIT_CLUSTER incompatible in 32 bit OS" ++#endif ++ ++ sbi->used.bitmap.nbits = clusters; ++ ++ rec = ntfs_zalloc(record_size); ++ if (!rec) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ sbi->new_rec = rec; ++ rec->rhdr.sign = NTFS_FILE_SIGNATURE; ++ rec->rhdr.fix_off = cpu_to_le16(MFTRECORD_FIXUP_OFFSET_1); ++ fn = (sbi->record_size >> SECTOR_SHIFT) + 1; ++ rec->rhdr.fix_num = cpu_to_le16(fn); ++ ao = QuadAlign(MFTRECORD_FIXUP_OFFSET_1 + sizeof(short) * fn); ++ rec->attr_off = cpu_to_le16(ao); ++ rec->used = cpu_to_le32(ao + QuadAlign(sizeof(enum ATTR_TYPE))); ++ rec->total = cpu_to_le32(sbi->record_size); ++ ((struct ATTRIB *)Add2Ptr(rec, ao))->type = ATTR_END; ++ ++ if (sbi->cluster_size < PAGE_SIZE) ++ sb_set_blocksize(sb, sbi->cluster_size); ++ ++ sbi->block_mask = sb->s_blocksize - 1; ++ sbi->blocks_per_cluster = sbi->cluster_size >> sb->s_blocksize_bits; ++ sbi->volume.blocks = sbi->volume.size >> sb->s_blocksize_bits; ++ ++ /* Maximum size for normal files */ ++ sbi->maxbytes = (clusters << sbi->cluster_bits) - 1; ++ ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++ if (clusters >= (1ull << (64 - sbi->cluster_bits))) ++ sbi->maxbytes = -1; ++ sbi->maxbytes_sparse = -1; ++#else ++ /* Maximum size for sparse file */ ++ sbi->maxbytes_sparse = (1ull << (sbi->cluster_bits + 32)) - 1; ++#endif ++ ++ err = 0; ++ ++out: ++ brelse(bh); ++ ++ return err; ++} ++ ++/* try to mount*/ ++static int ntfs_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ int err; ++ struct ntfs_sb_info *sbi; ++ struct block_device *bdev = sb->s_bdev; ++ struct inode *bd_inode = bdev->bd_inode; ++ struct request_queue *rq = bdev_get_queue(bdev); ++ struct inode *inode = NULL; ++ struct ntfs_inode *ni; ++ size_t i, tt; ++ CLST vcn, lcn, len; ++ struct ATTRIB *attr; ++ const struct VOLUME_INFO *info; ++ u32 idx, done, bytes; ++ struct ATTR_DEF_ENTRY *t; ++ u16 *upcase = NULL; ++ u16 *shared; ++ bool is_ro; ++ struct MFT_REF ref; ++ ++ ref.high = 0; ++ ++ sbi = ntfs_zalloc(sizeof(struct ntfs_sb_info)); ++ if (!sbi) ++ return -ENOMEM; ++ ++ sb->s_fs_info = sbi; ++ sbi->sb = sb; ++ sb->s_flags |= SB_NODIRATIME; ++ sb->s_magic = 0x7366746e; // "ntfs" ++ sb->s_op = &ntfs_sops; ++ sb->s_export_op = &ntfs_export_ops; ++ sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec ++ sb->s_xattr = ntfs_xattr_handlers; ++ ++ ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL, ++ DEFAULT_RATELIMIT_BURST); ++ ++ err = ntfs_parse_options(sb, data, silent, &sbi->options); ++ if (err) ++ goto out; ++ ++ if (!rq || !blk_queue_discard(rq) || !rq->limits.discard_granularity) { ++ ; ++ } else { ++ sbi->discard_granularity = rq->limits.discard_granularity; ++ sbi->discard_granularity_mask_inv = ++ ~(u64)(sbi->discard_granularity - 1); ++ } ++ ++ sb_set_blocksize(sb, PAGE_SIZE); ++ ++ /* parse boot */ ++ err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512, ++ bd_inode->i_size); ++ if (err) ++ goto out; ++ ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++ sb->s_maxbytes = MAX_LFS_FILESIZE; ++#else ++ sb->s_maxbytes = 0xFFFFFFFFull << sbi->cluster_bits; ++#endif ++ ++ mutex_init(&sbi->compress.mtx_lznt); ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ mutex_init(&sbi->compress.mtx_xpress); ++ mutex_init(&sbi->compress.mtx_lzx); ++#endif ++ ++ /* ++ * Load $Volume. This should be done before LogFile ++ * 'cause 'sbi->volume.ni' is used 'ntfs_set_state' ++ */ ++ ref.low = cpu_to_le32(MFT_REC_VOL); ++ ref.seq = cpu_to_le16(MFT_REC_VOL); ++ inode = ntfs_iget5(sb, &ref, &NAME_VOLUME); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $Volume."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ /* Load and save label (not necessary) */ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_LABEL, NULL, 0, NULL, NULL); ++ ++ if (!attr) { ++ /* It is ok if no ATTR_LABEL */ ++ } else if (!attr->non_res && !is_attr_ext(attr)) { ++ /* $AttrDef allows labels to be up to 128 symbols */ ++ err = utf16s_to_utf8s(resident_data(attr), ++ le32_to_cpu(attr->res.data_size) >> 1, ++ UTF16_LITTLE_ENDIAN, sbi->volume.label, ++ sizeof(sbi->volume.label)); ++ if (err < 0) ++ sbi->volume.label[0] = 0; ++ } else { ++ /* should we break mounting here? */ ++ //err = -EINVAL; ++ //goto out; ++ } ++ ++ attr = ni_find_attr(ni, attr, NULL, ATTR_VOL_INFO, NULL, 0, NULL, NULL); ++ if (!attr || is_attr_ext(attr)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ info = resident_data_ex(attr, SIZEOF_ATTRIBUTE_VOLUME_INFO); ++ if (!info) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ sbi->volume.major_ver = info->major_ver; ++ sbi->volume.minor_ver = info->minor_ver; ++ sbi->volume.flags = info->flags; ++ ++ sbi->volume.ni = ni; ++ inode = NULL; ++ ++ /* Load $MFTMirr to estimate recs_mirr */ ++ ref.low = cpu_to_le32(MFT_REC_MIRR); ++ ref.seq = cpu_to_le16(MFT_REC_MIRR); ++ inode = ntfs_iget5(sb, &ref, &NAME_MIRROR); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $MFTMirr."); ++ inode = NULL; ++ goto out; ++ } ++ ++ sbi->mft.recs_mirr = ++ ntfs_up_cluster(sbi, inode->i_size) >> sbi->record_bits; ++ ++ iput(inode); ++ ++ /* Load LogFile to replay */ ++ ref.low = cpu_to_le32(MFT_REC_LOG); ++ ref.seq = cpu_to_le16(MFT_REC_LOG); ++ inode = ntfs_iget5(sb, &ref, &NAME_LOGFILE); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load \x24LogFile."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ err = ntfs_loadlog_and_replay(ni, sbi); ++ if (err) ++ goto out; ++ ++ iput(inode); ++ inode = NULL; ++ ++ is_ro = sb_rdonly(sbi->sb); ++ ++ if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) { ++ if (!is_ro) { ++ ntfs_warn(sb, ++ "failed to replay log file. Can't mount rw!"); ++ err = -EINVAL; ++ goto out; ++ } ++ } else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) { ++ if (!is_ro && !sbi->options.force) { ++ ntfs_warn( ++ sb, ++ "volume is dirty and \"force\" flag is not set!"); ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ /* Load $MFT */ ++ ref.low = cpu_to_le32(MFT_REC_MFT); ++ ref.seq = cpu_to_le16(1); ++ ++ inode = ntfs_iget5(sb, &ref, &NAME_MFT); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $MFT."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ sbi->mft.used = ni->i_valid >> sbi->record_bits; ++ tt = inode->i_size >> sbi->record_bits; ++ sbi->mft.next_free = MFT_REC_USER; ++ ++ err = wnd_init(&sbi->mft.bitmap, sb, tt); ++ if (err) ++ goto out; ++ ++ err = ni_load_all_mi(ni); ++ if (err) ++ goto out; ++ ++ sbi->mft.ni = ni; ++ ++ /* Load $BadClus */ ++ ref.low = cpu_to_le32(MFT_REC_BADCLUST); ++ ref.seq = cpu_to_le16(MFT_REC_BADCLUST); ++ inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $BadClus."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ for (i = 0; run_get_entry(&ni->file.run, i, &vcn, &lcn, &len); i++) { ++ if (lcn == SPARSE_LCN) ++ continue; ++ ++ if (!sbi->bad_clusters) ++ ntfs_notice(sb, "Volume contains bad blocks"); ++ ++ sbi->bad_clusters += len; ++ } ++ ++ iput(inode); ++ ++ /* Load $Bitmap */ ++ ref.low = cpu_to_le32(MFT_REC_BITMAP); ++ ref.seq = cpu_to_le16(MFT_REC_BITMAP); ++ inode = ntfs_iget5(sb, &ref, &NAME_BITMAP); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $Bitmap."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++#ifndef CONFIG_NTFS3_64BIT_CLUSTER ++ if (inode->i_size >> 32) { ++ err = -EINVAL; ++ goto out; ++ } ++#endif ++ ++ /* Check bitmap boundary */ ++ tt = sbi->used.bitmap.nbits; ++ if (inode->i_size < bitmap_size(tt)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Not necessary */ ++ sbi->used.bitmap.set_tail = true; ++ err = wnd_init(&sbi->used.bitmap, sbi->sb, tt); ++ if (err) ++ goto out; ++ ++ iput(inode); ++ ++ /* Compute the mft zone */ ++ err = ntfs_refresh_zone(sbi); ++ if (err) ++ goto out; ++ ++ /* Load $AttrDef */ ++ ref.low = cpu_to_le32(MFT_REC_ATTR); ++ ref.seq = cpu_to_le16(MFT_REC_ATTR); ++ inode = ntfs_iget5(sbi->sb, &ref, &NAME_ATTRDEF); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load $AttrDef -> %d", err); ++ inode = NULL; ++ goto out; ++ } ++ ++ if (inode->i_size < sizeof(struct ATTR_DEF_ENTRY)) { ++ err = -EINVAL; ++ goto out; ++ } ++ bytes = inode->i_size; ++ sbi->def_table = t = ntfs_malloc(bytes); ++ if (!t) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ for (done = idx = 0; done < bytes; done += PAGE_SIZE, idx++) { ++ unsigned long tail = bytes - done; ++ struct page *page = ntfs_map_page(inode->i_mapping, idx); ++ ++ if (IS_ERR(page)) { ++ err = PTR_ERR(page); ++ goto out; ++ } ++ memcpy(Add2Ptr(t, done), page_address(page), ++ min(PAGE_SIZE, tail)); ++ ntfs_unmap_page(page); ++ ++ if (!idx && ATTR_STD != t->type) { ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ t += 1; ++ sbi->def_entries = 1; ++ done = sizeof(struct ATTR_DEF_ENTRY); ++ sbi->reparse.max_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; ++ sbi->ea_max_size = 0x10000; /* default formater value */ ++ ++ while (done + sizeof(struct ATTR_DEF_ENTRY) <= bytes) { ++ u32 t32 = le32_to_cpu(t->type); ++ u64 sz = le64_to_cpu(t->max_sz); ++ ++ if ((t32 & 0xF) || le32_to_cpu(t[-1].type) >= t32) ++ break; ++ ++ if (t->type == ATTR_REPARSE) ++ sbi->reparse.max_size = sz; ++ else if (t->type == ATTR_EA) ++ sbi->ea_max_size = sz; ++ ++ done += sizeof(struct ATTR_DEF_ENTRY); ++ t += 1; ++ sbi->def_entries += 1; ++ } ++ iput(inode); ++ ++ /* Load $UpCase */ ++ ref.low = cpu_to_le32(MFT_REC_UPCASE); ++ ref.seq = cpu_to_le16(MFT_REC_UPCASE); ++ inode = ntfs_iget5(sb, &ref, &NAME_UPCASE); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load \x24LogFile."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ if (inode->i_size != 0x10000 * sizeof(short)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ sbi->upcase = upcase = ntfs_vmalloc(0x10000 * sizeof(short)); ++ if (!upcase) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) { ++ const __le16 *src; ++ u16 *dst = Add2Ptr(upcase, idx << PAGE_SHIFT); ++ struct page *page = ntfs_map_page(inode->i_mapping, idx); ++ ++ if (IS_ERR(page)) { ++ err = PTR_ERR(page); ++ goto out; ++ } ++ ++ src = page_address(page); ++ ++#ifdef __BIG_ENDIAN ++ for (i = 0; i < PAGE_SIZE / sizeof(u16); i++) ++ *dst++ = le16_to_cpu(*src++); ++#else ++ memcpy(dst, src, PAGE_SIZE); ++#endif ++ ntfs_unmap_page(page); ++ } ++ ++ shared = ntfs_set_shared(upcase, 0x10000 * sizeof(short)); ++ if (shared && upcase != shared) { ++ sbi->upcase = shared; ++ ntfs_vfree(upcase); ++ } ++ ++ iput(inode); ++ inode = NULL; ++ ++ if (is_ntfs3(sbi)) { ++ /* Load $Secure */ ++ err = ntfs_security_init(sbi); ++ if (err) ++ goto out; ++ ++ /* Load $Extend */ ++ err = ntfs_extend_init(sbi); ++ if (err) ++ goto load_root; ++ ++ /* Load $Extend\$Reparse */ ++ err = ntfs_reparse_init(sbi); ++ if (err) ++ goto load_root; ++ ++ /* Load $Extend\$ObjId */ ++ err = ntfs_objid_init(sbi); ++ if (err) ++ goto load_root; ++ } ++ ++load_root: ++ /* Load root */ ++ ref.low = cpu_to_le32(MFT_REC_ROOT); ++ ref.seq = cpu_to_le16(MFT_REC_ROOT); ++ inode = ntfs_iget5(sb, &ref, &NAME_ROOT); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ ntfs_err(sb, "Failed to load root."); ++ inode = NULL; ++ goto out; ++ } ++ ++ ni = ntfs_i(inode); ++ ++ sb->s_root = d_make_root(inode); ++ ++ if (!sb->s_root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ return 0; ++ ++out: ++ iput(inode); ++ ++ if (sb->s_root) { ++ d_drop(sb->s_root); ++ sb->s_root = NULL; ++ } ++ ++ put_ntfs(sbi); ++ ++ sb->s_fs_info = NULL; ++ return err; ++} ++ ++void ntfs_unmap_meta(struct super_block *sb, CLST lcn, CLST len) ++{ ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct block_device *bdev = sb->s_bdev; ++ sector_t devblock = (u64)lcn * sbi->blocks_per_cluster; ++ unsigned long blocks = (u64)len * sbi->blocks_per_cluster; ++ unsigned long cnt = 0; ++ unsigned long limit = global_zone_page_state(NR_FREE_PAGES) ++ << (PAGE_SHIFT - sb->s_blocksize_bits); ++ ++ if (limit >= 0x2000) ++ limit -= 0x1000; ++ else if (limit < 32) ++ limit = 32; ++ else ++ limit >>= 1; ++ ++ while (blocks--) { ++ clean_bdev_aliases(bdev, devblock++, 1); ++ if (cnt++ >= limit) { ++ sync_blockdev(bdev); ++ cnt = 0; ++ } ++ } ++} ++ ++/* ++ * ntfs_discard ++ * ++ * issue a discard request (trim for SSD) ++ */ ++int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len) ++{ ++ int err; ++ u64 lbo, bytes, start, end; ++ struct super_block *sb; ++ ++ if (sbi->used.next_free_lcn == lcn + len) ++ sbi->used.next_free_lcn = lcn; ++ ++ if (sbi->flags & NTFS_FLAGS_NODISCARD) ++ return -EOPNOTSUPP; ++ ++ if (!sbi->options.discard) ++ return -EOPNOTSUPP; ++ ++ lbo = (u64)lcn << sbi->cluster_bits; ++ bytes = (u64)len << sbi->cluster_bits; ++ ++ /* Align up 'start' on discard_granularity */ ++ start = (lbo + sbi->discard_granularity - 1) & ++ sbi->discard_granularity_mask_inv; ++ /* Align down 'end' on discard_granularity */ ++ end = (lbo + bytes) & sbi->discard_granularity_mask_inv; ++ ++ sb = sbi->sb; ++ if (start >= end) ++ return 0; ++ ++ err = blkdev_issue_discard(sb->s_bdev, start >> 9, (end - start) >> 9, ++ GFP_NOFS, 0); ++ ++ if (err == -EOPNOTSUPP) ++ sbi->flags |= NTFS_FLAGS_NODISCARD; ++ ++ return err; ++} ++ ++static struct dentry *ntfs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, ntfs_fill_super); ++} ++ ++// clang-format off ++static struct file_system_type ntfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "ntfs3", ++ .mount = ntfs_mount, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, ++}; ++// clang-format on ++ ++static int __init init_ntfs_fs(void) ++{ ++ int err; ++ ++ pr_notice("ntfs3: Index binary search\n"); ++ pr_notice("ntfs3: Hot fix free clusters\n"); ++ pr_notice("ntfs3: Max link count %u\n", NTFS_LINK_MAX); ++ ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++ pr_notice("ntfs3: Enabled Linux POSIX ACLs support\n"); ++#endif ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++ pr_notice("ntfs3: Activated 64 bits per cluster\n"); ++#else ++ pr_notice("ntfs3: Activated 32 bits per cluster\n"); ++#endif ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ pr_notice("ntfs3: Read-only lzx/xpress compression included\n"); ++#endif ++ ++ err = ntfs3_init_bitmap(); ++ if (err) ++ return err; ++ ++ ntfs_inode_cachep = kmem_cache_create( ++ "ntfs_inode_cache", sizeof(struct ntfs_inode), 0, ++ (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT), ++ init_once); ++ if (!ntfs_inode_cachep) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ err = register_filesystem(&ntfs_fs_type); ++ if (err) ++ goto out; ++ ++ return 0; ++out: ++ kmem_cache_destroy(ntfs_inode_cachep); ++out1: ++ ntfs3_exit_bitmap(); ++ return err; ++} ++ ++static void __exit exit_ntfs_fs(void) ++{ ++ if (ntfs_inode_cachep) { ++ rcu_barrier(); ++ kmem_cache_destroy(ntfs_inode_cachep); ++ } ++ ++ unregister_filesystem(&ntfs_fs_type); ++ ntfs3_exit_bitmap(); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ntfs3 read/write filesystem"); ++MODULE_INFO(behaviour, "Index binary search"); ++MODULE_INFO(behaviour, "Hot fix free clusters"); ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++MODULE_INFO(behaviour, "Enabled Linux POSIX ACLs support"); ++#endif ++#ifdef CONFIG_NTFS3_64BIT_CLUSTER ++MODULE_INFO(cluster, "Activated 64 bits per cluster"); ++#else ++MODULE_INFO(cluster, "Activated 32 bits per cluster"); ++#endif ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++MODULE_INFO(compression, "Read-only lzx/xpress compression included"); ++#endif ++ ++MODULE_AUTHOR("Konstantin Komarov"); ++MODULE_ALIAS_FS("ntfs3"); ++ ++module_init(init_ntfs_fs); ++module_exit(exit_ntfs_fs); +-- +2.31.1 + diff --git a/SOURCES/0003-fs-ntfs3-Add-bitmap.patch b/SOURCES/0003-fs-ntfs3-Add-bitmap.patch new file mode 100644 index 0000000..55cfcd5 --- /dev/null +++ b/SOURCES/0003-fs-ntfs3-Add-bitmap.patch @@ -0,0 +1,1684 @@ +From 3f3b442b5ad2455507c9bfdacf39a3792eb3a6d0 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:29 +0300 +Subject: [Backport 3f3b442b5ad2] src: Add bitmap + +This adds bitmap + +Signed-off-by: Konstantin Komarov +--- + src/bitfunc.c | 135 ++++ + src/bitmap.c | 1519 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 1654 insertions(+) + create mode 100644 src/bitfunc.c + create mode 100644 src/bitmap.c + +diff --git a/src/bitfunc.c b/src/bitfunc.c +new file mode 100644 +index 0000000000000000000000000000000000000000..2de5faef2721d5bac9caf7ff81adec167ae21dea +--- /dev/null ++++ b/src/bitfunc.c +@@ -0,0 +1,135 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++#define BITS_IN_SIZE_T (sizeof(size_t) * 8) ++ ++/* ++ * fill_mask[i] - first i bits are '1' , i = 0,1,2,3,4,5,6,7,8 ++ * fill_mask[i] = 0xFF >> (8-i) ++ */ ++static const u8 fill_mask[] = { 0x00, 0x01, 0x03, 0x07, 0x0F, ++ 0x1F, 0x3F, 0x7F, 0xFF }; ++ ++/* ++ * zero_mask[i] - first i bits are '0' , i = 0,1,2,3,4,5,6,7,8 ++ * zero_mask[i] = 0xFF << i ++ */ ++static const u8 zero_mask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, ++ 0xE0, 0xC0, 0x80, 0x00 }; ++ ++/* ++ * are_bits_clear ++ * ++ * Returns true if all bits [bit, bit+nbits) are zeros "0" ++ */ ++bool are_bits_clear(const ulong *lmap, size_t bit, size_t nbits) ++{ ++ size_t pos = bit & 7; ++ const u8 *map = (u8 *)lmap + (bit >> 3); ++ ++ if (pos) { ++ if (8 - pos >= nbits) ++ return !nbits || !(*map & fill_mask[pos + nbits] & ++ zero_mask[pos]); ++ ++ if (*map++ & zero_mask[pos]) ++ return false; ++ nbits -= 8 - pos; ++ } ++ ++ pos = ((size_t)map) & (sizeof(size_t) - 1); ++ if (pos) { ++ pos = sizeof(size_t) - pos; ++ if (nbits >= pos * 8) { ++ for (nbits -= pos * 8; pos; pos--, map++) { ++ if (*map) ++ return false; ++ } ++ } ++ } ++ ++ for (pos = nbits / BITS_IN_SIZE_T; pos; pos--, map += sizeof(size_t)) { ++ if (*((size_t *)map)) ++ return false; ++ } ++ ++ for (pos = (nbits % BITS_IN_SIZE_T) >> 3; pos; pos--, map++) { ++ if (*map) ++ return false; ++ } ++ ++ pos = nbits & 7; ++ if (pos && (*map & fill_mask[pos])) ++ return false; ++ ++ // All bits are zero ++ return true; ++} ++ ++/* ++ * are_bits_set ++ * ++ * Returns true if all bits [bit, bit+nbits) are ones "1" ++ */ ++bool are_bits_set(const ulong *lmap, size_t bit, size_t nbits) ++{ ++ u8 mask; ++ size_t pos = bit & 7; ++ const u8 *map = (u8 *)lmap + (bit >> 3); ++ ++ if (pos) { ++ if (8 - pos >= nbits) { ++ mask = fill_mask[pos + nbits] & zero_mask[pos]; ++ return !nbits || (*map & mask) == mask; ++ } ++ ++ mask = zero_mask[pos]; ++ if ((*map++ & mask) != mask) ++ return false; ++ nbits -= 8 - pos; ++ } ++ ++ pos = ((size_t)map) & (sizeof(size_t) - 1); ++ if (pos) { ++ pos = sizeof(size_t) - pos; ++ if (nbits >= pos * 8) { ++ for (nbits -= pos * 8; pos; pos--, map++) { ++ if (*map != 0xFF) ++ return false; ++ } ++ } ++ } ++ ++ for (pos = nbits / BITS_IN_SIZE_T; pos; pos--, map += sizeof(size_t)) { ++ if (*((size_t *)map) != MINUS_ONE_T) ++ return false; ++ } ++ ++ for (pos = (nbits % BITS_IN_SIZE_T) >> 3; pos; pos--, map++) { ++ if (*map != 0xFF) ++ return false; ++ } ++ ++ pos = nbits & 7; ++ if (pos) { ++ u8 mask = fill_mask[pos]; ++ ++ if ((*map & mask) != mask) ++ return false; ++ } ++ ++ // All bits are ones ++ return true; ++} +diff --git a/src/bitmap.c b/src/bitmap.c +new file mode 100644 +index 0000000000000000000000000000000000000000..32aab0031221db3ee67b09c239b960158370d238 +--- /dev/null ++++ b/src/bitmap.c +@@ -0,0 +1,1519 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * This code builds two trees of free clusters extents. ++ * Trees are sorted by start of extent and by length of extent. ++ * NTFS_MAX_WND_EXTENTS defines the maximum number of elements in trees. ++ * In extreme case code reads on-disk bitmap to find free clusters ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* ++ * Maximum number of extents in tree. ++ */ ++#define NTFS_MAX_WND_EXTENTS (32u * 1024u) ++ ++struct rb_node_key { ++ struct rb_node node; ++ size_t key; ++}; ++ ++/* ++ * Tree is sorted by start (key) ++ */ ++struct e_node { ++ struct rb_node_key start; /* Tree sorted by start */ ++ struct rb_node_key count; /* Tree sorted by len*/ ++}; ++ ++static int wnd_rescan(struct wnd_bitmap *wnd); ++static struct buffer_head *wnd_map(struct wnd_bitmap *wnd, size_t iw); ++static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits); ++ ++static struct kmem_cache *ntfs_enode_cachep; ++ ++int __init ntfs3_init_bitmap(void) ++{ ++ ntfs_enode_cachep = ++ kmem_cache_create("ntfs3_enode_cache", sizeof(struct e_node), 0, ++ SLAB_RECLAIM_ACCOUNT, NULL); ++ return ntfs_enode_cachep ? 0 : -ENOMEM; ++} ++ ++void ntfs3_exit_bitmap(void) ++{ ++ kmem_cache_destroy(ntfs_enode_cachep); ++} ++ ++static inline u32 wnd_bits(const struct wnd_bitmap *wnd, size_t i) ++{ ++ return i + 1 == wnd->nwnd ? wnd->bits_last : wnd->sb->s_blocksize * 8; ++} ++ ++/* ++ * b_pos + b_len - biggest fragment ++ * Scan range [wpos wbits) window 'buf' ++ * Returns -1 if not found ++ */ ++static size_t wnd_scan(const ulong *buf, size_t wbit, u32 wpos, u32 wend, ++ size_t to_alloc, size_t *prev_tail, size_t *b_pos, ++ size_t *b_len) ++{ ++ while (wpos < wend) { ++ size_t free_len; ++ u32 free_bits, end; ++ u32 used = find_next_zero_bit(buf, wend, wpos); ++ ++ if (used >= wend) { ++ if (*b_len < *prev_tail) { ++ *b_pos = wbit - *prev_tail; ++ *b_len = *prev_tail; ++ } ++ ++ *prev_tail = 0; ++ return -1; ++ } ++ ++ if (used > wpos) { ++ wpos = used; ++ if (*b_len < *prev_tail) { ++ *b_pos = wbit - *prev_tail; ++ *b_len = *prev_tail; ++ } ++ ++ *prev_tail = 0; ++ } ++ ++ /* ++ * Now we have a fragment [wpos, wend) staring with 0 ++ */ ++ end = wpos + to_alloc - *prev_tail; ++ free_bits = find_next_bit(buf, min(end, wend), wpos); ++ ++ free_len = *prev_tail + free_bits - wpos; ++ ++ if (*b_len < free_len) { ++ *b_pos = wbit + wpos - *prev_tail; ++ *b_len = free_len; ++ } ++ ++ if (free_len >= to_alloc) ++ return wbit + wpos - *prev_tail; ++ ++ if (free_bits >= wend) { ++ *prev_tail += free_bits - wpos; ++ return -1; ++ } ++ ++ wpos = free_bits + 1; ++ ++ *prev_tail = 0; ++ } ++ ++ return -1; ++} ++ ++/* ++ * wnd_close ++ * ++ * Frees all resources ++ */ ++void wnd_close(struct wnd_bitmap *wnd) ++{ ++ struct rb_node *node, *next; ++ ++ ntfs_free(wnd->free_bits); ++ run_close(&wnd->run); ++ ++ node = rb_first(&wnd->start_tree); ++ ++ while (node) { ++ next = rb_next(node); ++ rb_erase(node, &wnd->start_tree); ++ kmem_cache_free(ntfs_enode_cachep, ++ rb_entry(node, struct e_node, start.node)); ++ node = next; ++ } ++} ++ ++static struct rb_node *rb_lookup(struct rb_root *root, size_t v) ++{ ++ struct rb_node **p = &root->rb_node; ++ struct rb_node *r = NULL; ++ ++ while (*p) { ++ struct rb_node_key *k; ++ ++ k = rb_entry(*p, struct rb_node_key, node); ++ if (v < k->key) { ++ p = &(*p)->rb_left; ++ } else if (v > k->key) { ++ r = &k->node; ++ p = &(*p)->rb_right; ++ } else { ++ return &k->node; ++ } ++ } ++ ++ return r; ++} ++ ++/* ++ * rb_insert_count ++ * ++ * Helper function to insert special kind of 'count' tree ++ */ ++static inline bool rb_insert_count(struct rb_root *root, struct e_node *e) ++{ ++ struct rb_node **p = &root->rb_node; ++ struct rb_node *parent = NULL; ++ size_t e_ckey = e->count.key; ++ size_t e_skey = e->start.key; ++ ++ while (*p) { ++ struct e_node *k = ++ rb_entry(parent = *p, struct e_node, count.node); ++ ++ if (e_ckey > k->count.key) { ++ p = &(*p)->rb_left; ++ } else if (e_ckey < k->count.key) { ++ p = &(*p)->rb_right; ++ } else if (e_skey < k->start.key) { ++ p = &(*p)->rb_left; ++ } else if (e_skey > k->start.key) { ++ p = &(*p)->rb_right; ++ } else { ++ WARN_ON(1); ++ return false; ++ } ++ } ++ ++ rb_link_node(&e->count.node, parent, p); ++ rb_insert_color(&e->count.node, root); ++ return true; ++} ++ ++/* ++ * inline bool rb_insert_start ++ * ++ * Helper function to insert special kind of 'start' tree ++ */ ++static inline bool rb_insert_start(struct rb_root *root, struct e_node *e) ++{ ++ struct rb_node **p = &root->rb_node; ++ struct rb_node *parent = NULL; ++ size_t e_skey = e->start.key; ++ ++ while (*p) { ++ struct e_node *k; ++ ++ parent = *p; ++ ++ k = rb_entry(parent, struct e_node, start.node); ++ if (e_skey < k->start.key) { ++ p = &(*p)->rb_left; ++ } else if (e_skey > k->start.key) { ++ p = &(*p)->rb_right; ++ } else { ++ WARN_ON(1); ++ return false; ++ } ++ } ++ ++ rb_link_node(&e->start.node, parent, p); ++ rb_insert_color(&e->start.node, root); ++ return true; ++} ++ ++/* ++ * wnd_add_free_ext ++ * ++ * adds a new extent of free space ++ * build = 1 when building tree ++ */ ++static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, ++ bool build) ++{ ++ struct e_node *e, *e0 = NULL; ++ size_t ib, end_in = bit + len; ++ struct rb_node *n; ++ ++ if (build) { ++ /* Use extent_min to filter too short extents */ ++ if (wnd->count >= NTFS_MAX_WND_EXTENTS && ++ len <= wnd->extent_min) { ++ wnd->uptodated = -1; ++ return; ++ } ++ } else { ++ /* Try to find extent before 'bit' */ ++ n = rb_lookup(&wnd->start_tree, bit); ++ ++ if (!n) { ++ n = rb_first(&wnd->start_tree); ++ } else { ++ e = rb_entry(n, struct e_node, start.node); ++ n = rb_next(n); ++ if (e->start.key + e->count.key == bit) { ++ /* Remove left */ ++ bit = e->start.key; ++ len += e->count.key; ++ rb_erase(&e->start.node, &wnd->start_tree); ++ rb_erase(&e->count.node, &wnd->count_tree); ++ wnd->count -= 1; ++ e0 = e; ++ } ++ } ++ ++ while (n) { ++ size_t next_end; ++ ++ e = rb_entry(n, struct e_node, start.node); ++ next_end = e->start.key + e->count.key; ++ if (e->start.key > end_in) ++ break; ++ ++ /* Remove right */ ++ n = rb_next(n); ++ len += next_end - end_in; ++ end_in = next_end; ++ rb_erase(&e->start.node, &wnd->start_tree); ++ rb_erase(&e->count.node, &wnd->count_tree); ++ wnd->count -= 1; ++ ++ if (!e0) ++ e0 = e; ++ else ++ kmem_cache_free(ntfs_enode_cachep, e); ++ } ++ ++ if (wnd->uptodated != 1) { ++ /* Check bits before 'bit' */ ++ ib = wnd->zone_bit == wnd->zone_end || ++ bit < wnd->zone_end ++ ? 0 ++ : wnd->zone_end; ++ ++ while (bit > ib && wnd_is_free_hlp(wnd, bit - 1, 1)) { ++ bit -= 1; ++ len += 1; ++ } ++ ++ /* Check bits after 'end_in' */ ++ ib = wnd->zone_bit == wnd->zone_end || ++ end_in > wnd->zone_bit ++ ? wnd->nbits ++ : wnd->zone_bit; ++ ++ while (end_in < ib && wnd_is_free_hlp(wnd, end_in, 1)) { ++ end_in += 1; ++ len += 1; ++ } ++ } ++ } ++ /* Insert new fragment */ ++ if (wnd->count >= NTFS_MAX_WND_EXTENTS) { ++ if (e0) ++ kmem_cache_free(ntfs_enode_cachep, e0); ++ ++ wnd->uptodated = -1; ++ ++ /* Compare with smallest fragment */ ++ n = rb_last(&wnd->count_tree); ++ e = rb_entry(n, struct e_node, count.node); ++ if (len <= e->count.key) ++ goto out; /* Do not insert small fragments */ ++ ++ if (build) { ++ struct e_node *e2; ++ ++ n = rb_prev(n); ++ e2 = rb_entry(n, struct e_node, count.node); ++ /* smallest fragment will be 'e2->count.key' */ ++ wnd->extent_min = e2->count.key; ++ } ++ ++ /* Replace smallest fragment by new one */ ++ rb_erase(&e->start.node, &wnd->start_tree); ++ rb_erase(&e->count.node, &wnd->count_tree); ++ wnd->count -= 1; ++ } else { ++ e = e0 ? e0 : kmem_cache_alloc(ntfs_enode_cachep, GFP_ATOMIC); ++ if (!e) { ++ wnd->uptodated = -1; ++ goto out; ++ } ++ ++ if (build && len <= wnd->extent_min) ++ wnd->extent_min = len; ++ } ++ e->start.key = bit; ++ e->count.key = len; ++ if (len > wnd->extent_max) ++ wnd->extent_max = len; ++ ++ rb_insert_start(&wnd->start_tree, e); ++ rb_insert_count(&wnd->count_tree, e); ++ wnd->count += 1; ++ ++out:; ++} ++ ++/* ++ * wnd_remove_free_ext ++ * ++ * removes a run from the cached free space ++ */ ++static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) ++{ ++ struct rb_node *n, *n3; ++ struct e_node *e, *e3; ++ size_t end_in = bit + len; ++ size_t end3, end, new_key, new_len, max_new_len; ++ ++ /* Try to find extent before 'bit' */ ++ n = rb_lookup(&wnd->start_tree, bit); ++ ++ if (!n) ++ return; ++ ++ e = rb_entry(n, struct e_node, start.node); ++ end = e->start.key + e->count.key; ++ ++ new_key = new_len = 0; ++ len = e->count.key; ++ ++ /* Range [bit,end_in) must be inside 'e' or outside 'e' and 'n' */ ++ if (e->start.key > bit) ++ ; ++ else if (end_in <= end) { ++ /* Range [bit,end_in) inside 'e' */ ++ new_key = end_in; ++ new_len = end - end_in; ++ len = bit - e->start.key; ++ } else if (bit > end) { ++ bool bmax = false; ++ ++ n3 = rb_next(n); ++ ++ while (n3) { ++ e3 = rb_entry(n3, struct e_node, start.node); ++ if (e3->start.key >= end_in) ++ break; ++ ++ if (e3->count.key == wnd->extent_max) ++ bmax = true; ++ ++ end3 = e3->start.key + e3->count.key; ++ if (end3 > end_in) { ++ e3->start.key = end_in; ++ rb_erase(&e3->count.node, &wnd->count_tree); ++ e3->count.key = end3 - end_in; ++ rb_insert_count(&wnd->count_tree, e3); ++ break; ++ } ++ ++ n3 = rb_next(n3); ++ rb_erase(&e3->start.node, &wnd->start_tree); ++ rb_erase(&e3->count.node, &wnd->count_tree); ++ wnd->count -= 1; ++ kmem_cache_free(ntfs_enode_cachep, e3); ++ } ++ if (!bmax) ++ return; ++ n3 = rb_first(&wnd->count_tree); ++ wnd->extent_max = ++ n3 ? rb_entry(n3, struct e_node, count.node)->count.key ++ : 0; ++ return; ++ } ++ ++ if (e->count.key != wnd->extent_max) { ++ ; ++ } else if (rb_prev(&e->count.node)) { ++ ; ++ } else { ++ n3 = rb_next(&e->count.node); ++ max_new_len = len > new_len ? len : new_len; ++ if (!n3) { ++ wnd->extent_max = max_new_len; ++ } else { ++ e3 = rb_entry(n3, struct e_node, count.node); ++ wnd->extent_max = max(e3->count.key, max_new_len); ++ } ++ } ++ ++ if (!len) { ++ if (new_len) { ++ e->start.key = new_key; ++ rb_erase(&e->count.node, &wnd->count_tree); ++ e->count.key = new_len; ++ rb_insert_count(&wnd->count_tree, e); ++ } else { ++ rb_erase(&e->start.node, &wnd->start_tree); ++ rb_erase(&e->count.node, &wnd->count_tree); ++ wnd->count -= 1; ++ kmem_cache_free(ntfs_enode_cachep, e); ++ } ++ goto out; ++ } ++ rb_erase(&e->count.node, &wnd->count_tree); ++ e->count.key = len; ++ rb_insert_count(&wnd->count_tree, e); ++ ++ if (!new_len) ++ goto out; ++ ++ if (wnd->count >= NTFS_MAX_WND_EXTENTS) { ++ wnd->uptodated = -1; ++ ++ /* Get minimal extent */ ++ e = rb_entry(rb_last(&wnd->count_tree), struct e_node, ++ count.node); ++ if (e->count.key > new_len) ++ goto out; ++ ++ /* Replace minimum */ ++ rb_erase(&e->start.node, &wnd->start_tree); ++ rb_erase(&e->count.node, &wnd->count_tree); ++ wnd->count -= 1; ++ } else { ++ e = kmem_cache_alloc(ntfs_enode_cachep, GFP_ATOMIC); ++ if (!e) ++ wnd->uptodated = -1; ++ } ++ ++ if (e) { ++ e->start.key = new_key; ++ e->count.key = new_len; ++ rb_insert_start(&wnd->start_tree, e); ++ rb_insert_count(&wnd->count_tree, e); ++ wnd->count += 1; ++ } ++ ++out: ++ if (!wnd->count && 1 != wnd->uptodated) ++ wnd_rescan(wnd); ++} ++ ++/* ++ * wnd_rescan ++ * ++ * Scan all bitmap. used while initialization. ++ */ ++static int wnd_rescan(struct wnd_bitmap *wnd) ++{ ++ int err = 0; ++ size_t prev_tail = 0; ++ struct super_block *sb = wnd->sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ u64 lbo, len = 0; ++ u32 blocksize = sb->s_blocksize; ++ u8 cluster_bits = sbi->cluster_bits; ++ u32 wbits = 8 * sb->s_blocksize; ++ u32 used, frb; ++ const ulong *buf; ++ size_t wpos, wbit, iw, vbo; ++ struct buffer_head *bh = NULL; ++ CLST lcn, clen; ++ ++ wnd->uptodated = 0; ++ wnd->extent_max = 0; ++ wnd->extent_min = MINUS_ONE_T; ++ wnd->total_zeroes = 0; ++ ++ vbo = 0; ++ ++ for (iw = 0; iw < wnd->nwnd; iw++) { ++ if (iw + 1 == wnd->nwnd) ++ wbits = wnd->bits_last; ++ ++ if (wnd->inited) { ++ if (!wnd->free_bits[iw]) { ++ /* all ones */ ++ if (prev_tail) { ++ wnd_add_free_ext(wnd, ++ vbo * 8 - prev_tail, ++ prev_tail, true); ++ prev_tail = 0; ++ } ++ goto next_wnd; ++ } ++ if (wbits == wnd->free_bits[iw]) { ++ /* all zeroes */ ++ prev_tail += wbits; ++ wnd->total_zeroes += wbits; ++ goto next_wnd; ++ } ++ } ++ ++ if (!len) { ++ u32 off = vbo & sbi->cluster_mask; ++ ++ if (!run_lookup_entry(&wnd->run, vbo >> cluster_bits, ++ &lcn, &clen, NULL)) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ lbo = ((u64)lcn << cluster_bits) + off; ++ len = ((u64)clen << cluster_bits) - off; ++ } ++ ++ bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits); ++ if (!bh) { ++ err = -EIO; ++ goto out; ++ } ++ ++ buf = (ulong *)bh->b_data; ++ ++ used = __bitmap_weight(buf, wbits); ++ if (used < wbits) { ++ frb = wbits - used; ++ wnd->free_bits[iw] = frb; ++ wnd->total_zeroes += frb; ++ } ++ ++ wpos = 0; ++ wbit = vbo * 8; ++ ++ if (wbit + wbits > wnd->nbits) ++ wbits = wnd->nbits - wbit; ++ ++ do { ++ used = find_next_zero_bit(buf, wbits, wpos); ++ ++ if (used > wpos && prev_tail) { ++ wnd_add_free_ext(wnd, wbit + wpos - prev_tail, ++ prev_tail, true); ++ prev_tail = 0; ++ } ++ ++ wpos = used; ++ ++ if (wpos >= wbits) { ++ /* No free blocks */ ++ prev_tail = 0; ++ break; ++ } ++ ++ frb = find_next_bit(buf, wbits, wpos); ++ if (frb >= wbits) { ++ /* keep last free block */ ++ prev_tail += frb - wpos; ++ break; ++ } ++ ++ wnd_add_free_ext(wnd, wbit + wpos - prev_tail, ++ frb + prev_tail - wpos, true); ++ ++ /* Skip free block and first '1' */ ++ wpos = frb + 1; ++ /* Reset previous tail */ ++ prev_tail = 0; ++ } while (wpos < wbits); ++ ++next_wnd: ++ ++ if (bh) ++ put_bh(bh); ++ bh = NULL; ++ ++ vbo += blocksize; ++ if (len) { ++ len -= blocksize; ++ lbo += blocksize; ++ } ++ } ++ ++ /* Add last block */ ++ if (prev_tail) ++ wnd_add_free_ext(wnd, wnd->nbits - prev_tail, prev_tail, true); ++ ++ /* ++ * Before init cycle wnd->uptodated was 0 ++ * If any errors or limits occurs while initialization then ++ * wnd->uptodated will be -1 ++ * If 'uptodated' is still 0 then Tree is really updated ++ */ ++ if (!wnd->uptodated) ++ wnd->uptodated = 1; ++ ++ if (wnd->zone_bit != wnd->zone_end) { ++ size_t zlen = wnd->zone_end - wnd->zone_bit; ++ ++ wnd->zone_end = wnd->zone_bit; ++ wnd_zone_set(wnd, wnd->zone_bit, zlen); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * wnd_init ++ */ ++int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits) ++{ ++ int err; ++ u32 blocksize = sb->s_blocksize; ++ u32 wbits = blocksize * 8; ++ ++ init_rwsem(&wnd->rw_lock); ++ ++ wnd->sb = sb; ++ wnd->nbits = nbits; ++ wnd->total_zeroes = nbits; ++ wnd->extent_max = MINUS_ONE_T; ++ wnd->zone_bit = wnd->zone_end = 0; ++ wnd->nwnd = bytes_to_block(sb, bitmap_size(nbits)); ++ wnd->bits_last = nbits & (wbits - 1); ++ if (!wnd->bits_last) ++ wnd->bits_last = wbits; ++ ++ wnd->free_bits = ntfs_zalloc(wnd->nwnd * sizeof(u16)); ++ if (!wnd->free_bits) ++ return -ENOMEM; ++ ++ err = wnd_rescan(wnd); ++ if (err) ++ return err; ++ ++ wnd->inited = true; ++ ++ return 0; ++} ++ ++/* ++ * wnd_map ++ * ++ * call sb_bread for requested window ++ */ ++static struct buffer_head *wnd_map(struct wnd_bitmap *wnd, size_t iw) ++{ ++ size_t vbo; ++ CLST lcn, clen; ++ struct super_block *sb = wnd->sb; ++ struct ntfs_sb_info *sbi; ++ struct buffer_head *bh; ++ u64 lbo; ++ ++ sbi = sb->s_fs_info; ++ vbo = (u64)iw << sb->s_blocksize_bits; ++ ++ if (!run_lookup_entry(&wnd->run, vbo >> sbi->cluster_bits, &lcn, &clen, ++ NULL)) { ++ return ERR_PTR(-ENOENT); ++ } ++ ++ lbo = ((u64)lcn << sbi->cluster_bits) + (vbo & sbi->cluster_mask); ++ ++ bh = ntfs_bread(wnd->sb, lbo >> sb->s_blocksize_bits); ++ if (!bh) ++ return ERR_PTR(-EIO); ++ ++ return bh; ++} ++ ++/* ++ * wnd_set_free ++ * ++ * Marks the bits range from bit to bit + bits as free ++ */ ++int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) ++{ ++ int err = 0; ++ struct super_block *sb = wnd->sb; ++ size_t bits0 = bits; ++ u32 wbits = 8 * sb->s_blocksize; ++ size_t iw = bit >> (sb->s_blocksize_bits + 3); ++ u32 wbit = bit & (wbits - 1); ++ struct buffer_head *bh; ++ ++ while (iw < wnd->nwnd && bits) { ++ u32 tail, op; ++ ulong *buf; ++ ++ if (iw + 1 == wnd->nwnd) ++ wbits = wnd->bits_last; ++ ++ tail = wbits - wbit; ++ op = tail < bits ? tail : bits; ++ ++ bh = wnd_map(wnd, iw); ++ if (IS_ERR(bh)) { ++ err = PTR_ERR(bh); ++ break; ++ } ++ ++ buf = (ulong *)bh->b_data; ++ ++ lock_buffer(bh); ++ ++ __bitmap_clear(buf, wbit, op); ++ ++ wnd->free_bits[iw] += op; ++ ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ unlock_buffer(bh); ++ put_bh(bh); ++ ++ wnd->total_zeroes += op; ++ bits -= op; ++ wbit = 0; ++ iw += 1; ++ } ++ ++ wnd_add_free_ext(wnd, bit, bits0, false); ++ ++ return err; ++} ++ ++/* ++ * wnd_set_used ++ * ++ * Marks the bits range from bit to bit + bits as used ++ */ ++int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) ++{ ++ int err = 0; ++ struct super_block *sb = wnd->sb; ++ size_t bits0 = bits; ++ size_t iw = bit >> (sb->s_blocksize_bits + 3); ++ u32 wbits = 8 * sb->s_blocksize; ++ u32 wbit = bit & (wbits - 1); ++ struct buffer_head *bh; ++ ++ while (iw < wnd->nwnd && bits) { ++ u32 tail, op; ++ ulong *buf; ++ ++ if (unlikely(iw + 1 == wnd->nwnd)) ++ wbits = wnd->bits_last; ++ ++ tail = wbits - wbit; ++ op = tail < bits ? tail : bits; ++ ++ bh = wnd_map(wnd, iw); ++ if (IS_ERR(bh)) { ++ err = PTR_ERR(bh); ++ break; ++ } ++ buf = (ulong *)bh->b_data; ++ ++ lock_buffer(bh); ++ ++ __bitmap_set(buf, wbit, op); ++ wnd->free_bits[iw] -= op; ++ ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ unlock_buffer(bh); ++ put_bh(bh); ++ ++ wnd->total_zeroes -= op; ++ bits -= op; ++ wbit = 0; ++ iw += 1; ++ } ++ ++ if (!RB_EMPTY_ROOT(&wnd->start_tree)) ++ wnd_remove_free_ext(wnd, bit, bits0); ++ ++ return err; ++} ++ ++/* ++ * wnd_is_free_hlp ++ * ++ * Returns true if all clusters [bit, bit+bits) are free (bitmap only) ++ */ ++static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits) ++{ ++ struct super_block *sb = wnd->sb; ++ size_t iw = bit >> (sb->s_blocksize_bits + 3); ++ u32 wbits = 8 * sb->s_blocksize; ++ u32 wbit = bit & (wbits - 1); ++ ++ while (iw < wnd->nwnd && bits) { ++ u32 tail, op; ++ ++ if (unlikely(iw + 1 == wnd->nwnd)) ++ wbits = wnd->bits_last; ++ ++ tail = wbits - wbit; ++ op = tail < bits ? tail : bits; ++ ++ if (wbits != wnd->free_bits[iw]) { ++ bool ret; ++ struct buffer_head *bh = wnd_map(wnd, iw); ++ ++ if (IS_ERR(bh)) ++ return false; ++ ++ ret = are_bits_clear((ulong *)bh->b_data, wbit, op); ++ ++ put_bh(bh); ++ if (!ret) ++ return false; ++ } ++ ++ bits -= op; ++ wbit = 0; ++ iw += 1; ++ } ++ ++ return true; ++} ++ ++/* ++ * wnd_is_free ++ * ++ * Returns true if all clusters [bit, bit+bits) are free ++ */ ++bool wnd_is_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) ++{ ++ bool ret; ++ struct rb_node *n; ++ size_t end; ++ struct e_node *e; ++ ++ if (RB_EMPTY_ROOT(&wnd->start_tree)) ++ goto use_wnd; ++ ++ n = rb_lookup(&wnd->start_tree, bit); ++ if (!n) ++ goto use_wnd; ++ ++ e = rb_entry(n, struct e_node, start.node); ++ ++ end = e->start.key + e->count.key; ++ ++ if (bit < end && bit + bits <= end) ++ return true; ++ ++use_wnd: ++ ret = wnd_is_free_hlp(wnd, bit, bits); ++ ++ return ret; ++} ++ ++/* ++ * wnd_is_used ++ * ++ * Returns true if all clusters [bit, bit+bits) are used ++ */ ++bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) ++{ ++ bool ret = false; ++ struct super_block *sb = wnd->sb; ++ size_t iw = bit >> (sb->s_blocksize_bits + 3); ++ u32 wbits = 8 * sb->s_blocksize; ++ u32 wbit = bit & (wbits - 1); ++ size_t end; ++ struct rb_node *n; ++ struct e_node *e; ++ ++ if (RB_EMPTY_ROOT(&wnd->start_tree)) ++ goto use_wnd; ++ ++ end = bit + bits; ++ n = rb_lookup(&wnd->start_tree, end - 1); ++ if (!n) ++ goto use_wnd; ++ ++ e = rb_entry(n, struct e_node, start.node); ++ if (e->start.key + e->count.key > bit) ++ return false; ++ ++use_wnd: ++ while (iw < wnd->nwnd && bits) { ++ u32 tail, op; ++ ++ if (unlikely(iw + 1 == wnd->nwnd)) ++ wbits = wnd->bits_last; ++ ++ tail = wbits - wbit; ++ op = tail < bits ? tail : bits; ++ ++ if (wnd->free_bits[iw]) { ++ bool ret; ++ struct buffer_head *bh = wnd_map(wnd, iw); ++ ++ if (IS_ERR(bh)) ++ goto out; ++ ++ ret = are_bits_set((ulong *)bh->b_data, wbit, op); ++ put_bh(bh); ++ if (!ret) ++ goto out; ++ } ++ ++ bits -= op; ++ wbit = 0; ++ iw += 1; ++ } ++ ret = true; ++ ++out: ++ return ret; ++} ++ ++/* ++ * wnd_find ++ * - flags - BITMAP_FIND_XXX flags ++ * ++ * looks for free space ++ * Returns 0 if not found ++ */ ++size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, ++ size_t flags, size_t *allocated) ++{ ++ struct super_block *sb; ++ u32 wbits, wpos, wzbit, wzend; ++ size_t fnd, max_alloc, b_len, b_pos; ++ size_t iw, prev_tail, nwnd, wbit, ebit, zbit, zend; ++ size_t to_alloc0 = to_alloc; ++ const ulong *buf; ++ const struct e_node *e; ++ const struct rb_node *pr, *cr; ++ u8 log2_bits; ++ bool fbits_valid; ++ struct buffer_head *bh; ++ ++ /* fast checking for available free space */ ++ if (flags & BITMAP_FIND_FULL) { ++ size_t zeroes = wnd_zeroes(wnd); ++ ++ zeroes -= wnd->zone_end - wnd->zone_bit; ++ if (zeroes < to_alloc0) ++ goto no_space; ++ ++ if (to_alloc0 > wnd->extent_max) ++ goto no_space; ++ } else { ++ if (to_alloc > wnd->extent_max) ++ to_alloc = wnd->extent_max; ++ } ++ ++ if (wnd->zone_bit <= hint && hint < wnd->zone_end) ++ hint = wnd->zone_end; ++ ++ max_alloc = wnd->nbits; ++ b_len = b_pos = 0; ++ ++ if (hint >= max_alloc) ++ hint = 0; ++ ++ if (RB_EMPTY_ROOT(&wnd->start_tree)) { ++ if (wnd->uptodated == 1) { ++ /* extents tree is updated -> no free space */ ++ goto no_space; ++ } ++ goto scan_bitmap; ++ } ++ ++ e = NULL; ++ if (!hint) ++ goto allocate_biggest; ++ ++ /* Use hint: enumerate extents by start >= hint */ ++ pr = NULL; ++ cr = wnd->start_tree.rb_node; ++ ++ for (;;) { ++ e = rb_entry(cr, struct e_node, start.node); ++ ++ if (e->start.key == hint) ++ break; ++ ++ if (e->start.key < hint) { ++ pr = cr; ++ cr = cr->rb_right; ++ if (!cr) ++ break; ++ continue; ++ } ++ ++ cr = cr->rb_left; ++ if (!cr) { ++ e = pr ? rb_entry(pr, struct e_node, start.node) : NULL; ++ break; ++ } ++ } ++ ++ if (!e) ++ goto allocate_biggest; ++ ++ if (e->start.key + e->count.key > hint) { ++ /* We have found extension with 'hint' inside */ ++ size_t len = e->start.key + e->count.key - hint; ++ ++ if (len >= to_alloc && hint + to_alloc <= max_alloc) { ++ fnd = hint; ++ goto found; ++ } ++ ++ if (!(flags & BITMAP_FIND_FULL)) { ++ if (len > to_alloc) ++ len = to_alloc; ++ ++ if (hint + len <= max_alloc) { ++ fnd = hint; ++ to_alloc = len; ++ goto found; ++ } ++ } ++ } ++ ++allocate_biggest: ++ /* Allocate from biggest free extent */ ++ e = rb_entry(rb_first(&wnd->count_tree), struct e_node, count.node); ++ if (e->count.key != wnd->extent_max) ++ wnd->extent_max = e->count.key; ++ ++ if (e->count.key < max_alloc) { ++ if (e->count.key >= to_alloc) { ++ ; ++ } else if (flags & BITMAP_FIND_FULL) { ++ if (e->count.key < to_alloc0) { ++ /* Biggest free block is less then requested */ ++ goto no_space; ++ } ++ to_alloc = e->count.key; ++ } else if (-1 != wnd->uptodated) { ++ to_alloc = e->count.key; ++ } else { ++ /* Check if we can use more bits */ ++ size_t op, max_check; ++ struct rb_root start_tree; ++ ++ memcpy(&start_tree, &wnd->start_tree, ++ sizeof(struct rb_root)); ++ memset(&wnd->start_tree, 0, sizeof(struct rb_root)); ++ ++ max_check = e->start.key + to_alloc; ++ if (max_check > max_alloc) ++ max_check = max_alloc; ++ for (op = e->start.key + e->count.key; op < max_check; ++ op++) { ++ if (!wnd_is_free(wnd, op, 1)) ++ break; ++ } ++ memcpy(&wnd->start_tree, &start_tree, ++ sizeof(struct rb_root)); ++ to_alloc = op - e->start.key; ++ } ++ ++ /* Prepare to return */ ++ fnd = e->start.key; ++ if (e->start.key + to_alloc > max_alloc) ++ to_alloc = max_alloc - e->start.key; ++ goto found; ++ } ++ ++ if (wnd->uptodated == 1) { ++ /* extents tree is updated -> no free space */ ++ goto no_space; ++ } ++ ++ b_len = e->count.key; ++ b_pos = e->start.key; ++ ++scan_bitmap: ++ sb = wnd->sb; ++ log2_bits = sb->s_blocksize_bits + 3; ++ ++ /* At most two ranges [hint, max_alloc) + [0, hint) */ ++Again: ++ ++ /* TODO: optimize request for case nbits > wbits */ ++ iw = hint >> log2_bits; ++ wbits = sb->s_blocksize * 8; ++ wpos = hint & (wbits - 1); ++ prev_tail = 0; ++ fbits_valid = true; ++ ++ if (max_alloc == wnd->nbits) { ++ nwnd = wnd->nwnd; ++ } else { ++ size_t t = max_alloc + wbits - 1; ++ ++ nwnd = likely(t > max_alloc) ? (t >> log2_bits) : wnd->nwnd; ++ } ++ ++ /* Enumerate all windows */ ++ for (; iw < nwnd; iw++) { ++ wbit = iw << log2_bits; ++ ++ if (!wnd->free_bits[iw]) { ++ if (prev_tail > b_len) { ++ b_pos = wbit - prev_tail; ++ b_len = prev_tail; ++ } ++ ++ /* Skip full used window */ ++ prev_tail = 0; ++ wpos = 0; ++ continue; ++ } ++ ++ if (unlikely(iw + 1 == nwnd)) { ++ if (max_alloc == wnd->nbits) { ++ wbits = wnd->bits_last; ++ } else { ++ size_t t = max_alloc & (wbits - 1); ++ ++ if (t) { ++ wbits = t; ++ fbits_valid = false; ++ } ++ } ++ } ++ ++ if (wnd->zone_end > wnd->zone_bit) { ++ ebit = wbit + wbits; ++ zbit = max(wnd->zone_bit, wbit); ++ zend = min(wnd->zone_end, ebit); ++ ++ /* Here we have a window [wbit, ebit) and zone [zbit, zend) */ ++ if (zend <= zbit) { ++ /* Zone does not overlap window */ ++ } else { ++ wzbit = zbit - wbit; ++ wzend = zend - wbit; ++ ++ /* Zone overlaps window */ ++ if (wnd->free_bits[iw] == wzend - wzbit) { ++ prev_tail = 0; ++ wpos = 0; ++ continue; ++ } ++ ++ /* Scan two ranges window: [wbit, zbit) and [zend, ebit) */ ++ bh = wnd_map(wnd, iw); ++ ++ if (IS_ERR(bh)) { ++ /* TODO: error */ ++ prev_tail = 0; ++ wpos = 0; ++ continue; ++ } ++ ++ buf = (ulong *)bh->b_data; ++ ++ /* Scan range [wbit, zbit) */ ++ if (wpos < wzbit) { ++ /* Scan range [wpos, zbit) */ ++ fnd = wnd_scan(buf, wbit, wpos, wzbit, ++ to_alloc, &prev_tail, ++ &b_pos, &b_len); ++ if (fnd != MINUS_ONE_T) { ++ put_bh(bh); ++ goto found; ++ } ++ } ++ ++ prev_tail = 0; ++ ++ /* Scan range [zend, ebit) */ ++ if (wzend < wbits) { ++ fnd = wnd_scan(buf, wbit, ++ max(wzend, wpos), wbits, ++ to_alloc, &prev_tail, ++ &b_pos, &b_len); ++ if (fnd != MINUS_ONE_T) { ++ put_bh(bh); ++ goto found; ++ } ++ } ++ ++ wpos = 0; ++ put_bh(bh); ++ continue; ++ } ++ } ++ ++ /* Current window does not overlap zone */ ++ if (!wpos && fbits_valid && wnd->free_bits[iw] == wbits) { ++ /* window is empty */ ++ if (prev_tail + wbits >= to_alloc) { ++ fnd = wbit + wpos - prev_tail; ++ goto found; ++ } ++ ++ /* Increase 'prev_tail' and process next window */ ++ prev_tail += wbits; ++ wpos = 0; ++ continue; ++ } ++ ++ /* read window */ ++ bh = wnd_map(wnd, iw); ++ if (IS_ERR(bh)) { ++ // TODO: error ++ prev_tail = 0; ++ wpos = 0; ++ continue; ++ } ++ ++ buf = (ulong *)bh->b_data; ++ ++ /* Scan range [wpos, eBits) */ ++ fnd = wnd_scan(buf, wbit, wpos, wbits, to_alloc, &prev_tail, ++ &b_pos, &b_len); ++ put_bh(bh); ++ if (fnd != MINUS_ONE_T) ++ goto found; ++ } ++ ++ if (b_len < prev_tail) { ++ /* The last fragment */ ++ b_len = prev_tail; ++ b_pos = max_alloc - prev_tail; ++ } ++ ++ if (hint) { ++ /* ++ * We have scanned range [hint max_alloc) ++ * Prepare to scan range [0 hint + to_alloc) ++ */ ++ size_t nextmax = hint + to_alloc; ++ ++ if (likely(nextmax >= hint) && nextmax < max_alloc) ++ max_alloc = nextmax; ++ hint = 0; ++ goto Again; ++ } ++ ++ if (!b_len) ++ goto no_space; ++ ++ wnd->extent_max = b_len; ++ ++ if (flags & BITMAP_FIND_FULL) ++ goto no_space; ++ ++ fnd = b_pos; ++ to_alloc = b_len; ++ ++found: ++ if (flags & BITMAP_FIND_MARK_AS_USED) { ++ /* TODO optimize remove extent (pass 'e'?) */ ++ if (wnd_set_used(wnd, fnd, to_alloc)) ++ goto no_space; ++ } else if (wnd->extent_max != MINUS_ONE_T && ++ to_alloc > wnd->extent_max) { ++ wnd->extent_max = to_alloc; ++ } ++ ++ *allocated = fnd; ++ return to_alloc; ++ ++no_space: ++ return 0; ++} ++ ++/* ++ * wnd_extend ++ * ++ * Extend bitmap ($MFT bitmap) ++ */ ++int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) ++{ ++ int err; ++ struct super_block *sb = wnd->sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ u32 blocksize = sb->s_blocksize; ++ u32 wbits = blocksize * 8; ++ u32 b0, new_last; ++ size_t bits, iw, new_wnd; ++ size_t old_bits = wnd->nbits; ++ u16 *new_free; ++ ++ if (new_bits <= old_bits) ++ return -EINVAL; ++ ++ /* align to 8 byte boundary */ ++ new_wnd = bytes_to_block(sb, bitmap_size(new_bits)); ++ new_last = new_bits & (wbits - 1); ++ if (!new_last) ++ new_last = wbits; ++ ++ if (new_wnd != wnd->nwnd) { ++ new_free = ntfs_malloc(new_wnd * sizeof(u16)); ++ if (!new_free) ++ return -ENOMEM; ++ ++ if (new_free != wnd->free_bits) ++ memcpy(new_free, wnd->free_bits, ++ wnd->nwnd * sizeof(short)); ++ memset(new_free + wnd->nwnd, 0, ++ (new_wnd - wnd->nwnd) * sizeof(short)); ++ ntfs_free(wnd->free_bits); ++ wnd->free_bits = new_free; ++ } ++ ++ /* Zero bits [old_bits,new_bits) */ ++ bits = new_bits - old_bits; ++ b0 = old_bits & (wbits - 1); ++ ++ for (iw = old_bits >> (sb->s_blocksize_bits + 3); bits; iw += 1) { ++ u32 op; ++ size_t frb; ++ u64 vbo, lbo, bytes; ++ struct buffer_head *bh; ++ ulong *buf; ++ ++ if (iw + 1 == new_wnd) ++ wbits = new_last; ++ ++ op = b0 + bits > wbits ? wbits - b0 : bits; ++ vbo = (u64)iw * blocksize; ++ ++ err = ntfs_vbo_to_lbo(sbi, &wnd->run, vbo, &lbo, &bytes); ++ if (err) ++ break; ++ ++ bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits); ++ if (!bh) ++ return -EIO; ++ ++ lock_buffer(bh); ++ buf = (ulong *)bh->b_data; ++ ++ __bitmap_clear(buf, b0, blocksize * 8 - b0); ++ frb = wbits - __bitmap_weight(buf, wbits); ++ wnd->total_zeroes += frb - wnd->free_bits[iw]; ++ wnd->free_bits[iw] = frb; ++ ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ unlock_buffer(bh); ++ /*err = sync_dirty_buffer(bh);*/ ++ ++ b0 = 0; ++ bits -= op; ++ } ++ ++ wnd->nbits = new_bits; ++ wnd->nwnd = new_wnd; ++ wnd->bits_last = new_last; ++ ++ wnd_add_free_ext(wnd, old_bits, new_bits - old_bits, false); ++ ++ return 0; ++} ++ ++/* ++ * wnd_zone_set ++ */ ++void wnd_zone_set(struct wnd_bitmap *wnd, size_t lcn, size_t len) ++{ ++ size_t zlen; ++ ++ zlen = wnd->zone_end - wnd->zone_bit; ++ if (zlen) ++ wnd_add_free_ext(wnd, wnd->zone_bit, zlen, false); ++ ++ if (!RB_EMPTY_ROOT(&wnd->start_tree) && len) ++ wnd_remove_free_ext(wnd, lcn, len); ++ ++ wnd->zone_bit = lcn; ++ wnd->zone_end = lcn + len; ++} ++ ++int ntfs_trim_fs(struct ntfs_sb_info *sbi, struct fstrim_range *range) ++{ ++ int err = 0; ++ struct super_block *sb = sbi->sb; ++ struct wnd_bitmap *wnd = &sbi->used.bitmap; ++ u32 wbits = 8 * sb->s_blocksize; ++ CLST len = 0, lcn = 0, done = 0; ++ CLST minlen = bytes_to_cluster(sbi, range->minlen); ++ CLST lcn_from = bytes_to_cluster(sbi, range->start); ++ size_t iw = lcn_from >> (sb->s_blocksize_bits + 3); ++ u32 wbit = lcn_from & (wbits - 1); ++ const ulong *buf; ++ CLST lcn_to; ++ ++ if (!minlen) ++ minlen = 1; ++ ++ if (range->len == (u64)-1) ++ lcn_to = wnd->nbits; ++ else ++ lcn_to = bytes_to_cluster(sbi, range->start + range->len); ++ ++ down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); ++ ++ for (; iw < wnd->nbits; iw++, wbit = 0) { ++ CLST lcn_wnd = iw * wbits; ++ struct buffer_head *bh; ++ ++ if (lcn_wnd > lcn_to) ++ break; ++ ++ if (!wnd->free_bits[iw]) ++ continue; ++ ++ if (iw + 1 == wnd->nwnd) ++ wbits = wnd->bits_last; ++ ++ if (lcn_wnd + wbits > lcn_to) ++ wbits = lcn_to - lcn_wnd; ++ ++ bh = wnd_map(wnd, iw); ++ if (IS_ERR(bh)) { ++ err = PTR_ERR(bh); ++ break; ++ } ++ ++ buf = (ulong *)bh->b_data; ++ ++ for (; wbit < wbits; wbit++) { ++ if (!test_bit(wbit, buf)) { ++ if (!len) ++ lcn = lcn_wnd + wbit; ++ len += 1; ++ continue; ++ } ++ if (len >= minlen) { ++ err = ntfs_discard(sbi, lcn, len); ++ if (err) ++ goto out; ++ done += len; ++ } ++ len = 0; ++ } ++ put_bh(bh); ++ } ++ ++ /* Process the last fragment */ ++ if (len >= minlen) { ++ err = ntfs_discard(sbi, lcn, len); ++ if (err) ++ goto out; ++ done += len; ++ } ++ ++out: ++ range->len = (u64)done << sbi->cluster_bits; ++ ++ up_read(&wnd->rw_lock); ++ ++ return err; ++} +-- +2.31.1 + diff --git a/SOURCES/0004-fs-ntfs3-Add-file-operations-and-implementation.patch b/SOURCES/0004-fs-ntfs3-Add-file-operations-and-implementation.patch new file mode 100644 index 0000000..72f8615 --- /dev/null +++ b/SOURCES/0004-fs-ntfs3-Add-file-operations-and-implementation.patch @@ -0,0 +1,7241 @@ +From 4342306f0f0d5ff4315a204d315c1b51b914fca5 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:29 +0300 +Subject: [Backport 4342306f0f0d] src: Add file operations and + implementation + +This adds file operations and implementation + +Signed-off-by: Konstantin Komarov +--- + src/dir.c | 596 +++++++++ + src/file.c | 1252 ++++++++++++++++++ + src/frecord.c | 3071 ++++++++++++++++++++++++++++++++++++++++++++ + src/namei.c | 539 ++++++++ + src/record.c | 609 +++++++++ + src/run.c | 1111 ++++++++++++++++ + 6 files changed, 7178 insertions(+) + create mode 100644 src/dir.c + create mode 100644 src/file.c + create mode 100644 src/frecord.c + create mode 100644 src/namei.c + create mode 100644 src/record.c + create mode 100644 src/run.c + +diff --git a/src/dir.c b/src/dir.c +new file mode 100644 +index 0000000000000000000000000000000000000000..d36d7fbc2b1dded9e1dea068a3f7ccd2783607ce +--- /dev/null ++++ b/src/dir.c +@@ -0,0 +1,596 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * directory handling functions for ntfs-based filesystems ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* ++ * Convert little endian utf16 to nls string ++ */ ++int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, ++ u8 *buf, int buf_len) ++{ ++ int ret, uni_len, warn; ++ const __le16 *ip; ++ u8 *op; ++ struct nls_table *nls = sbi->options.nls; ++ ++ static_assert(sizeof(wchar_t) == sizeof(__le16)); ++ ++ if (!nls) { ++ /* utf16 -> utf8 */ ++ ret = utf16s_to_utf8s((wchar_t *)uni->name, uni->len, ++ UTF16_LITTLE_ENDIAN, buf, buf_len); ++ buf[ret] = '\0'; ++ return ret; ++ } ++ ++ ip = uni->name; ++ op = buf; ++ uni_len = uni->len; ++ warn = 0; ++ ++ while (uni_len--) { ++ u16 ec; ++ int charlen; ++ char dump[5]; ++ ++ if (buf_len < NLS_MAX_CHARSET_SIZE) { ++ ntfs_warn(sbi->sb, ++ "filename was truncated while converting."); ++ break; ++ } ++ ++ ec = le16_to_cpu(*ip++); ++ charlen = nls->uni2char(ec, op, buf_len); ++ ++ if (charlen > 0) { ++ op += charlen; ++ buf_len -= charlen; ++ continue; ++ } ++ ++ *op++ = '_'; ++ buf_len -= 1; ++ if (warn) ++ continue; ++ ++ warn = 1; ++ hex_byte_pack(&dump[0], ec >> 8); ++ hex_byte_pack(&dump[2], ec); ++ dump[4] = 0; ++ ++ ntfs_err(sbi->sb, "failed to convert \"%s\" to %s", dump, ++ nls->charset); ++ } ++ ++ *op = '\0'; ++ return op - buf; ++} ++ ++// clang-format off ++#define PLANE_SIZE 0x00010000 ++ ++#define SURROGATE_PAIR 0x0000d800 ++#define SURROGATE_LOW 0x00000400 ++#define SURROGATE_BITS 0x000003ff ++// clang-format on ++ ++/* ++ * modified version of put_utf16 from fs/nls/nls_base.c ++ * is sparse warnings free ++ */ ++static inline void put_utf16(wchar_t *s, unsigned int c, ++ enum utf16_endian endian) ++{ ++ static_assert(sizeof(wchar_t) == sizeof(__le16)); ++ static_assert(sizeof(wchar_t) == sizeof(__be16)); ++ ++ switch (endian) { ++ default: ++ *s = (wchar_t)c; ++ break; ++ case UTF16_LITTLE_ENDIAN: ++ *(__le16 *)s = __cpu_to_le16(c); ++ break; ++ case UTF16_BIG_ENDIAN: ++ *(__be16 *)s = __cpu_to_be16(c); ++ break; ++ } ++} ++ ++/* ++ * modified version of 'utf8s_to_utf16s' allows to ++ * detect -ENAMETOOLONG without writing out of expected maximum ++ */ ++static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian, ++ wchar_t *pwcs, int maxout) ++{ ++ u16 *op; ++ int size; ++ unicode_t u; ++ ++ op = pwcs; ++ while (inlen > 0 && *s) { ++ if (*s & 0x80) { ++ size = utf8_to_utf32(s, inlen, &u); ++ if (size < 0) ++ return -EINVAL; ++ s += size; ++ inlen -= size; ++ ++ if (u >= PLANE_SIZE) { ++ if (maxout < 2) ++ return -ENAMETOOLONG; ++ ++ u -= PLANE_SIZE; ++ put_utf16(op++, ++ SURROGATE_PAIR | ++ ((u >> 10) & SURROGATE_BITS), ++ endian); ++ put_utf16(op++, ++ SURROGATE_PAIR | SURROGATE_LOW | ++ (u & SURROGATE_BITS), ++ endian); ++ maxout -= 2; ++ } else { ++ if (maxout < 1) ++ return -ENAMETOOLONG; ++ ++ put_utf16(op++, u, endian); ++ maxout--; ++ } ++ } else { ++ if (maxout < 1) ++ return -ENAMETOOLONG; ++ ++ put_utf16(op++, *s++, endian); ++ inlen--; ++ maxout--; ++ } ++ } ++ return op - pwcs; ++} ++ ++/* ++ * Convert input string to utf16 ++ * ++ * name, name_len - input name ++ * uni, max_ulen - destination memory ++ * endian - endian of target utf16 string ++ * ++ * This function is called: ++ * - to create ntfs name ++ * - to create symlink ++ * ++ * returns utf16 string length or error (if negative) ++ */ ++int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, ++ struct cpu_str *uni, u32 max_ulen, ++ enum utf16_endian endian) ++{ ++ int ret, slen; ++ const u8 *end; ++ struct nls_table *nls = sbi->options.nls; ++ u16 *uname = uni->name; ++ ++ static_assert(sizeof(wchar_t) == sizeof(u16)); ++ ++ if (!nls) { ++ /* utf8 -> utf16 */ ++ ret = _utf8s_to_utf16s(name, name_len, endian, uname, max_ulen); ++ uni->len = ret; ++ return ret; ++ } ++ ++ for (ret = 0, end = name + name_len; name < end; ret++, name += slen) { ++ if (ret >= max_ulen) ++ return -ENAMETOOLONG; ++ ++ slen = nls->char2uni(name, end - name, uname + ret); ++ if (!slen) ++ return -EINVAL; ++ if (slen < 0) ++ return slen; ++ } ++ ++#ifdef __BIG_ENDIAN ++ if (endian == UTF16_LITTLE_ENDIAN) { ++ int i = ret; ++ ++ while (i--) { ++ __cpu_to_le16s(uname); ++ uname++; ++ } ++ } ++#else ++ if (endian == UTF16_BIG_ENDIAN) { ++ int i = ret; ++ ++ while (i--) { ++ __cpu_to_be16s(uname); ++ uname++; ++ } ++ } ++#endif ++ ++ uni->len = ret; ++ return ret; ++} ++ ++/* helper function */ ++struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, ++ struct ntfs_fnd *fnd) ++{ ++ int err = 0; ++ struct super_block *sb = dir->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_inode *ni = ntfs_i(dir); ++ struct NTFS_DE *e; ++ int diff; ++ struct inode *inode = NULL; ++ struct ntfs_fnd *fnd_a = NULL; ++ ++ if (!fnd) { ++ fnd_a = fnd_get(); ++ if (!fnd_a) { ++ err = -ENOMEM; ++ goto out; ++ } ++ fnd = fnd_a; ++ } ++ ++ err = indx_find(&ni->dir, ni, NULL, uni, 0, sbi, &diff, &e, fnd); ++ ++ if (err) ++ goto out; ++ ++ if (diff) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ inode = ntfs_iget5(sb, &e->ref, uni); ++ if (!IS_ERR(inode) && is_bad_inode(inode)) { ++ iput(inode); ++ err = -EINVAL; ++ } ++out: ++ fnd_put(fnd_a); ++ ++ return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode; ++} ++ ++static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, ++ const struct NTFS_DE *e, u8 *name, ++ struct dir_context *ctx) ++{ ++ const struct ATTR_FILE_NAME *fname; ++ unsigned long ino; ++ int name_len; ++ u32 dt_type; ++ ++ fname = Add2Ptr(e, sizeof(struct NTFS_DE)); ++ ++ if (fname->type == FILE_NAME_DOS) ++ return 0; ++ ++ if (!mi_is_ref(&ni->mi, &fname->home)) ++ return 0; ++ ++ ino = ino_get(&e->ref); ++ ++ if (ino == MFT_REC_ROOT) ++ return 0; ++ ++ /* Skip meta files ( unless option to show metafiles is set ) */ ++ if (!sbi->options.showmeta && ntfs_is_meta_file(sbi, ino)) ++ return 0; ++ ++ if (sbi->options.nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) ++ return 0; ++ ++ name_len = ntfs_utf16_to_nls(sbi, (struct le_str *)&fname->name_len, ++ name, PATH_MAX); ++ if (name_len <= 0) { ++ ntfs_warn(sbi->sb, "failed to convert name for inode %lx.", ++ ino); ++ return 0; ++ } ++ ++ dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG; ++ ++ return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type); ++} ++ ++/* ++ * ntfs_read_hdr ++ * ++ * helper function 'ntfs_readdir' ++ */ ++static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, ++ const struct INDEX_HDR *hdr, u64 vbo, u64 pos, ++ u8 *name, struct dir_context *ctx) ++{ ++ int err; ++ const struct NTFS_DE *e; ++ u32 e_size; ++ u32 end = le32_to_cpu(hdr->used); ++ u32 off = le32_to_cpu(hdr->de_off); ++ ++ for (;; off += e_size) { ++ if (off + sizeof(struct NTFS_DE) > end) ++ return -1; ++ ++ e = Add2Ptr(hdr, off); ++ e_size = le16_to_cpu(e->size); ++ if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) ++ return -1; ++ ++ if (de_is_last(e)) ++ return 0; ++ ++ /* Skip already enumerated*/ ++ if (vbo + off < pos) ++ continue; ++ ++ if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME) ++ return -1; ++ ++ ctx->pos = vbo + off; ++ ++ /* Submit the name to the filldir callback. */ ++ err = ntfs_filldir(sbi, ni, e, name, ctx); ++ if (err) ++ return err; ++ } ++} ++ ++/* ++ * file_operations::iterate_shared ++ * ++ * Use non sorted enumeration. ++ * We have an example of broken volume where sorted enumeration ++ * counts each name twice ++ */ ++static int ntfs_readdir(struct file *file, struct dir_context *ctx) ++{ ++ const struct INDEX_ROOT *root; ++ u64 vbo; ++ size_t bit; ++ loff_t eod; ++ int err = 0; ++ struct inode *dir = file_inode(file); ++ struct ntfs_inode *ni = ntfs_i(dir); ++ struct super_block *sb = dir->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ loff_t i_size = i_size_read(dir); ++ u32 pos = ctx->pos; ++ u8 *name = NULL; ++ struct indx_node *node = NULL; ++ u8 index_bits = ni->dir.index_bits; ++ ++ /* name is a buffer of PATH_MAX length */ ++ static_assert(NTFS_NAME_LEN * 4 < PATH_MAX); ++ ++ eod = i_size + sbi->record_size; ++ ++ if (pos >= eod) ++ return 0; ++ ++ if (!dir_emit_dots(file, ctx)) ++ return 0; ++ ++ /* allocate PATH_MAX bytes */ ++ name = __getname(); ++ if (!name) ++ return -ENOMEM; ++ ++ if (!ni->mi_loaded && ni->attr_list.size) { ++ /* ++ * directory inode is locked for read ++ * load all subrecords to avoid 'write' access to 'ni' during ++ * directory reading ++ */ ++ ni_lock(ni); ++ if (!ni->mi_loaded && ni->attr_list.size) { ++ err = ni_load_all_mi(ni); ++ if (!err) ++ ni->mi_loaded = true; ++ } ++ ni_unlock(ni); ++ if (err) ++ goto out; ++ } ++ ++ root = indx_get_root(&ni->dir, ni, NULL, NULL); ++ if (!root) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (pos >= sbi->record_size) { ++ bit = (pos - sbi->record_size) >> index_bits; ++ } else { ++ err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx); ++ if (err) ++ goto out; ++ bit = 0; ++ } ++ ++ if (!i_size) { ++ ctx->pos = eod; ++ goto out; ++ } ++ ++ for (;;) { ++ vbo = (u64)bit << index_bits; ++ if (vbo >= i_size) { ++ ctx->pos = eod; ++ goto out; ++ } ++ ++ err = indx_used_bit(&ni->dir, ni, &bit); ++ if (err) ++ goto out; ++ ++ if (bit == MINUS_ONE_T) { ++ ctx->pos = eod; ++ goto out; ++ } ++ ++ vbo = (u64)bit << index_bits; ++ if (vbo >= i_size) { ++ ntfs_inode_err(dir, "Looks like your dir is corrupt"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits, ++ &node); ++ if (err) ++ goto out; ++ ++ err = ntfs_read_hdr(sbi, ni, &node->index->ihdr, ++ vbo + sbi->record_size, pos, name, ctx); ++ if (err) ++ goto out; ++ ++ bit += 1; ++ } ++ ++out: ++ ++ __putname(name); ++ put_indx_node(node); ++ ++ if (err == -ENOENT) { ++ err = 0; ++ ctx->pos = pos; ++ } ++ ++ return err; ++} ++ ++static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs, ++ size_t *files) ++{ ++ int err = 0; ++ struct ntfs_inode *ni = ntfs_i(dir); ++ struct NTFS_DE *e = NULL; ++ struct INDEX_ROOT *root; ++ struct INDEX_HDR *hdr; ++ const struct ATTR_FILE_NAME *fname; ++ u32 e_size, off, end; ++ u64 vbo = 0; ++ size_t drs = 0, fles = 0, bit = 0; ++ loff_t i_size = ni->vfs_inode.i_size; ++ struct indx_node *node = NULL; ++ u8 index_bits = ni->dir.index_bits; ++ ++ if (is_empty) ++ *is_empty = true; ++ ++ root = indx_get_root(&ni->dir, ni, NULL, NULL); ++ if (!root) ++ return -EINVAL; ++ ++ hdr = &root->ihdr; ++ ++ for (;;) { ++ end = le32_to_cpu(hdr->used); ++ off = le32_to_cpu(hdr->de_off); ++ ++ for (; off + sizeof(struct NTFS_DE) <= end; off += e_size) { ++ e = Add2Ptr(hdr, off); ++ e_size = le16_to_cpu(e->size); ++ if (e_size < sizeof(struct NTFS_DE) || ++ off + e_size > end) ++ break; ++ ++ if (de_is_last(e)) ++ break; ++ ++ fname = de_get_fname(e); ++ if (!fname) ++ continue; ++ ++ if (fname->type == FILE_NAME_DOS) ++ continue; ++ ++ if (is_empty) { ++ *is_empty = false; ++ if (!dirs && !files) ++ goto out; ++ } ++ ++ if (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ++ drs += 1; ++ else ++ fles += 1; ++ } ++ ++ if (vbo >= i_size) ++ goto out; ++ ++ err = indx_used_bit(&ni->dir, ni, &bit); ++ if (err) ++ goto out; ++ ++ if (bit == MINUS_ONE_T) ++ goto out; ++ ++ vbo = (u64)bit << index_bits; ++ if (vbo >= i_size) ++ goto out; ++ ++ err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits, ++ &node); ++ if (err) ++ goto out; ++ ++ hdr = &node->index->ihdr; ++ bit += 1; ++ vbo = (u64)bit << ni->dir.idx2vbn_bits; ++ } ++ ++out: ++ put_indx_node(node); ++ if (dirs) ++ *dirs = drs; ++ if (files) ++ *files = fles; ++ ++ return err; ++} ++ ++bool dir_is_empty(struct inode *dir) ++{ ++ bool is_empty = false; ++ ++ ntfs_dir_count(dir, &is_empty, NULL, NULL); ++ ++ return is_empty; ++} ++ ++// clang-format off ++const struct file_operations ntfs_dir_operations = { ++ .llseek = generic_file_llseek, ++ .read = generic_read_dir, ++ .iterate_shared = ntfs_readdir, ++ .fsync = generic_file_fsync, ++ .open = ntfs_file_open, ++}; ++// clang-format on +diff --git a/src/file.c b/src/file.c +new file mode 100644 +index 0000000000000000000000000000000000000000..59344985c2e8017d67cfba642d1bacaa676b3f24 +--- /dev/null ++++ b/src/file.c +@@ -0,0 +1,1252 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * regular file handling primitives for ntfs-based filesystems ++ */ ++#include ++#include ++#include ++#include ++#include ++#include /* FAT_IOCTL_XXX */ ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg) ++{ ++ struct fstrim_range __user *user_range; ++ struct fstrim_range range; ++ struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); ++ int err; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (!blk_queue_discard(q)) ++ return -EOPNOTSUPP; ++ ++ user_range = (struct fstrim_range __user *)arg; ++ if (copy_from_user(&range, user_range, sizeof(range))) ++ return -EFAULT; ++ ++ range.minlen = max_t(u32, range.minlen, q->limits.discard_granularity); ++ ++ err = ntfs_trim_fs(sbi, &range); ++ if (err < 0) ++ return err; ++ ++ if (copy_to_user(user_range, &range, sizeof(range))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) ++{ ++ struct inode *inode = file_inode(filp); ++ struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; ++ u32 __user *user_attr = (u32 __user *)arg; ++ ++ switch (cmd) { ++ case FAT_IOCTL_GET_ATTRIBUTES: ++ return put_user(le32_to_cpu(ntfs_i(inode)->std_fa), user_attr); ++ ++ case FAT_IOCTL_GET_VOLUME_ID: ++ return put_user(sbi->volume.ser_num, user_attr); ++ ++ case FITRIM: ++ return ntfs_ioctl_fitrim(sbi, arg); ++ } ++ return -ENOTTY; /* Inappropriate ioctl for device */ ++} ++ ++#ifdef CONFIG_COMPAT ++static long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg) ++ ++{ ++ return ntfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); ++} ++#endif ++ ++/* ++ * inode_operations::getattr ++ */ ++int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, ++ struct kstat *stat, u32 request_mask, u32 flags) ++{ ++ struct inode *inode = d_inode(path->dentry); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (is_compressed(ni)) ++ stat->attributes |= STATX_ATTR_COMPRESSED; ++ ++ if (is_encrypted(ni)) ++ stat->attributes |= STATX_ATTR_ENCRYPTED; ++ ++ stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED; ++ ++ generic_fillattr(mnt_userns, inode, stat); ++ ++ stat->result_mask |= STATX_BTIME; ++ stat->btime = ni->i_crtime; ++ stat->blksize = ni->mi.sbi->cluster_size; /* 512, 1K, ..., 2M */ ++ ++ return 0; ++} ++ ++static int ntfs_extend_initialized_size(struct file *file, ++ struct ntfs_inode *ni, ++ const loff_t valid, ++ const loff_t new_valid) ++{ ++ struct inode *inode = &ni->vfs_inode; ++ struct address_space *mapping = inode->i_mapping; ++ struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; ++ loff_t pos = valid; ++ int err; ++ ++ if (is_resident(ni)) { ++ ni->i_valid = new_valid; ++ return 0; ++ } ++ ++ WARN_ON(is_compressed(ni)); ++ WARN_ON(valid >= new_valid); ++ ++ for (;;) { ++ u32 zerofrom, len; ++ struct page *page; ++ void *fsdata; ++ u8 bits; ++ CLST vcn, lcn, clen; ++ ++ if (is_sparsed(ni)) { ++ bits = sbi->cluster_bits; ++ vcn = pos >> bits; ++ ++ err = attr_data_get_block(ni, vcn, 0, &lcn, &clen, ++ NULL); ++ if (err) ++ goto out; ++ ++ if (lcn == SPARSE_LCN) { ++ loff_t vbo = (loff_t)vcn << bits; ++ loff_t to = vbo + ((loff_t)clen << bits); ++ ++ if (to <= new_valid) { ++ ni->i_valid = to; ++ pos = to; ++ goto next; ++ } ++ ++ if (vbo < pos) { ++ pos = vbo; ++ } else { ++ to = (new_valid >> bits) << bits; ++ if (pos < to) { ++ ni->i_valid = to; ++ pos = to; ++ goto next; ++ } ++ } ++ } ++ } ++ ++ zerofrom = pos & (PAGE_SIZE - 1); ++ len = PAGE_SIZE - zerofrom; ++ ++ if (pos + len > new_valid) ++ len = new_valid - pos; ++ ++ err = pagecache_write_begin(file, mapping, pos, len, 0, &page, ++ &fsdata); ++ if (err) ++ goto out; ++ ++ zero_user_segment(page, zerofrom, PAGE_SIZE); ++ ++ /* this function in any case puts page*/ ++ err = pagecache_write_end(file, mapping, pos, len, len, page, ++ fsdata); ++ if (err < 0) ++ goto out; ++ pos += len; ++ ++next: ++ if (pos >= new_valid) ++ break; ++ ++ balance_dirty_pages_ratelimited(mapping); ++ cond_resched(); ++ } ++ ++ return 0; ++ ++out: ++ ni->i_valid = valid; ++ ntfs_inode_warn(inode, "failed to extend initialized size to %llx.", ++ new_valid); ++ return err; ++} ++ ++/* ++ * ntfs_zero_range ++ * ++ * Helper function for punch_hole. ++ * It zeroes a range [vbo, vbo_to) ++ */ ++static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) ++{ ++ int err = 0; ++ struct address_space *mapping = inode->i_mapping; ++ u32 blocksize = 1 << inode->i_blkbits; ++ pgoff_t idx = vbo >> PAGE_SHIFT; ++ u32 z_start = vbo & (PAGE_SIZE - 1); ++ pgoff_t idx_end = (vbo_to + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ loff_t page_off; ++ struct buffer_head *head, *bh; ++ u32 bh_next, bh_off, z_end; ++ sector_t iblock; ++ struct page *page; ++ ++ for (; idx < idx_end; idx += 1, z_start = 0) { ++ page_off = (loff_t)idx << PAGE_SHIFT; ++ z_end = (page_off + PAGE_SIZE) > vbo_to ? (vbo_to - page_off) ++ : PAGE_SIZE; ++ iblock = page_off >> inode->i_blkbits; ++ ++ page = find_or_create_page(mapping, idx, ++ mapping_gfp_constraint(mapping, ++ ~__GFP_FS)); ++ if (!page) ++ return -ENOMEM; ++ ++ if (!page_has_buffers(page)) ++ create_empty_buffers(page, blocksize, 0); ++ ++ bh = head = page_buffers(page); ++ bh_off = 0; ++ do { ++ bh_next = bh_off + blocksize; ++ ++ if (bh_next <= z_start || bh_off >= z_end) ++ continue; ++ ++ if (!buffer_mapped(bh)) { ++ ntfs_get_block(inode, iblock, bh, 0); ++ /* unmapped? It's a hole - nothing to do */ ++ if (!buffer_mapped(bh)) ++ continue; ++ } ++ ++ /* Ok, it's mapped. Make sure it's up-to-date */ ++ if (PageUptodate(page)) ++ set_buffer_uptodate(bh); ++ ++ if (!buffer_uptodate(bh)) { ++ lock_buffer(bh); ++ bh->b_end_io = end_buffer_read_sync; ++ get_bh(bh); ++ submit_bh(REQ_OP_READ, 0, bh); ++ ++ wait_on_buffer(bh); ++ if (!buffer_uptodate(bh)) { ++ unlock_page(page); ++ put_page(page); ++ err = -EIO; ++ goto out; ++ } ++ } ++ ++ mark_buffer_dirty(bh); ++ ++ } while (bh_off = bh_next, iblock += 1, ++ head != (bh = bh->b_this_page)); ++ ++ zero_user_segment(page, z_start, z_end); ++ ++ unlock_page(page); ++ put_page(page); ++ cond_resched(); ++ } ++out: ++ mark_inode_dirty(inode); ++ return err; ++} ++ ++/* ++ * ntfs_sparse_cluster ++ * ++ * Helper function to zero a new allocated clusters ++ * NOTE: 512 <= cluster size <= 2M ++ */ ++void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn, ++ CLST len) ++{ ++ struct address_space *mapping = inode->i_mapping; ++ struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; ++ u64 vbo = (u64)vcn << sbi->cluster_bits; ++ u64 bytes = (u64)len << sbi->cluster_bits; ++ u32 blocksize = 1 << inode->i_blkbits; ++ pgoff_t idx0 = page0 ? page0->index : -1; ++ loff_t vbo_clst = vbo & sbi->cluster_mask_inv; ++ loff_t end = ntfs_up_cluster(sbi, vbo + bytes); ++ pgoff_t idx = vbo_clst >> PAGE_SHIFT; ++ u32 from = vbo_clst & (PAGE_SIZE - 1); ++ pgoff_t idx_end = (end + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ loff_t page_off; ++ u32 to; ++ bool partial; ++ struct page *page; ++ ++ for (; idx < idx_end; idx += 1, from = 0) { ++ page = idx == idx0 ? page0 : grab_cache_page(mapping, idx); ++ ++ if (!page) ++ continue; ++ ++ page_off = (loff_t)idx << PAGE_SHIFT; ++ to = (page_off + PAGE_SIZE) > end ? (end - page_off) ++ : PAGE_SIZE; ++ partial = false; ++ ++ if ((from || PAGE_SIZE != to) && ++ likely(!page_has_buffers(page))) { ++ create_empty_buffers(page, blocksize, 0); ++ } ++ ++ if (page_has_buffers(page)) { ++ struct buffer_head *head, *bh; ++ u32 bh_off = 0; ++ ++ bh = head = page_buffers(page); ++ do { ++ u32 bh_next = bh_off + blocksize; ++ ++ if (from <= bh_off && bh_next <= to) { ++ set_buffer_uptodate(bh); ++ mark_buffer_dirty(bh); ++ } else if (!buffer_uptodate(bh)) { ++ partial = true; ++ } ++ bh_off = bh_next; ++ } while (head != (bh = bh->b_this_page)); ++ } ++ ++ zero_user_segment(page, from, to); ++ ++ if (!partial) { ++ if (!PageUptodate(page)) ++ SetPageUptodate(page); ++ set_page_dirty(page); ++ } ++ ++ if (idx != idx0) { ++ unlock_page(page); ++ put_page(page); ++ } ++ cond_resched(); ++ } ++ mark_inode_dirty(inode); ++} ++ ++/* ++ * file_operations::mmap ++ */ ++static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct address_space *mapping = file->f_mapping; ++ struct inode *inode = mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ u64 from = ((u64)vma->vm_pgoff << PAGE_SHIFT); ++ bool rw = vma->vm_flags & VM_WRITE; ++ int err; ++ ++ if (is_encrypted(ni)) { ++ ntfs_inode_warn(inode, "mmap encrypted not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (is_dedup(ni)) { ++ ntfs_inode_warn(inode, "mmap deduplicated not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (is_compressed(ni) && rw) { ++ ntfs_inode_warn(inode, "mmap(write) compressed not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (rw) { ++ u64 to = min_t(loff_t, i_size_read(inode), ++ from + vma->vm_end - vma->vm_start); ++ ++ if (is_sparsed(ni)) { ++ /* allocate clusters for rw map */ ++ struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; ++ CLST lcn, len; ++ CLST vcn = from >> sbi->cluster_bits; ++ CLST end = bytes_to_cluster(sbi, to); ++ bool new; ++ ++ for (; vcn < end; vcn += len) { ++ err = attr_data_get_block(ni, vcn, 1, &lcn, ++ &len, &new); ++ if (err) ++ goto out; ++ ++ if (!new) ++ continue; ++ ntfs_sparse_cluster(inode, NULL, vcn, 1); ++ } ++ } ++ ++ if (ni->i_valid < to) { ++ if (!inode_trylock(inode)) { ++ err = -EAGAIN; ++ goto out; ++ } ++ err = ntfs_extend_initialized_size(file, ni, ++ ni->i_valid, to); ++ inode_unlock(inode); ++ if (err) ++ goto out; ++ } ++ } ++ ++ err = generic_file_mmap(file, vma); ++out: ++ return err; ++} ++ ++static int ntfs_extend(struct inode *inode, loff_t pos, size_t count, ++ struct file *file) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct address_space *mapping = inode->i_mapping; ++ loff_t end = pos + count; ++ bool extend_init = file && pos > ni->i_valid; ++ int err; ++ ++ if (end <= inode->i_size && !extend_init) ++ return 0; ++ ++ /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY); ++ ++ if (end > inode->i_size) { ++ err = ntfs_set_size(inode, end); ++ if (err) ++ goto out; ++ inode->i_size = end; ++ } ++ ++ if (extend_init && !is_compressed(ni)) { ++ err = ntfs_extend_initialized_size(file, ni, ni->i_valid, pos); ++ if (err) ++ goto out; ++ } else { ++ err = 0; ++ } ++ ++ inode->i_ctime = inode->i_mtime = current_time(inode); ++ mark_inode_dirty(inode); ++ ++ if (IS_SYNC(inode)) { ++ int err2; ++ ++ err = filemap_fdatawrite_range(mapping, pos, end - 1); ++ err2 = sync_mapping_buffers(mapping); ++ if (!err) ++ err = err2; ++ err2 = write_inode_now(inode, 1); ++ if (!err) ++ err = err2; ++ if (!err) ++ err = filemap_fdatawait_range(mapping, pos, end - 1); ++ } ++ ++out: ++ return err; ++} ++ ++static int ntfs_truncate(struct inode *inode, loff_t new_size) ++{ ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ int err, dirty = 0; ++ u64 new_valid; ++ ++ if (!S_ISREG(inode->i_mode)) ++ return 0; ++ ++ if (is_compressed(ni)) { ++ if (ni->i_valid > new_size) ++ ni->i_valid = new_size; ++ } else { ++ err = block_truncate_page(inode->i_mapping, new_size, ++ ntfs_get_block); ++ if (err) ++ return err; ++ } ++ ++ new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size)); ++ ++ ni_lock(ni); ++ ++ truncate_setsize(inode, new_size); ++ ++ down_write(&ni->file.run_lock); ++ err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size, ++ &new_valid, true, NULL); ++ up_write(&ni->file.run_lock); ++ ++ if (new_valid < ni->i_valid) ++ ni->i_valid = new_valid; ++ ++ ni_unlock(ni); ++ ++ ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE; ++ inode->i_ctime = inode->i_mtime = current_time(inode); ++ if (!IS_DIRSYNC(inode)) { ++ dirty = 1; ++ } else { ++ err = ntfs_sync_inode(inode); ++ if (err) ++ return err; ++ } ++ ++ if (dirty) ++ mark_inode_dirty(inode); ++ ++ /*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/ ++ ++ return 0; ++} ++ ++/* ++ * Preallocate space for a file. This implements ntfs's fallocate file ++ * operation, which gets called from sys_fallocate system call. User ++ * space requests 'len' bytes at 'vbo'. If FALLOC_FL_KEEP_SIZE is set ++ * we just allocate clusters without zeroing them out. Otherwise we ++ * allocate and zero out clusters via an expanding truncate. ++ */ ++static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) ++{ ++ struct inode *inode = file->f_mapping->host; ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ loff_t end = vbo + len; ++ loff_t vbo_down = round_down(vbo, PAGE_SIZE); ++ loff_t i_size; ++ int err; ++ ++ /* No support for dir */ ++ if (!S_ISREG(inode->i_mode)) ++ return -EOPNOTSUPP; ++ ++ /* Return error if mode is not supported */ ++ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | ++ FALLOC_FL_COLLAPSE_RANGE)) { ++ ntfs_inode_warn(inode, "fallocate(0x%x) is not supported", ++ mode); ++ return -EOPNOTSUPP; ++ } ++ ++ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); ++ ++ inode_lock(inode); ++ i_size = inode->i_size; ++ ++ if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { ++ /* should never be here, see ntfs_file_open*/ ++ err = -EOPNOTSUPP; ++ goto out; ++ } ++ ++ if (mode & FALLOC_FL_PUNCH_HOLE) { ++ u32 frame_size; ++ loff_t mask, vbo_a, end_a, tmp; ++ ++ if (!(mode & FALLOC_FL_KEEP_SIZE)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = filemap_write_and_wait_range(inode->i_mapping, vbo, ++ end - 1); ++ if (err) ++ goto out; ++ ++ err = filemap_write_and_wait_range(inode->i_mapping, end, ++ LLONG_MAX); ++ if (err) ++ goto out; ++ ++ inode_dio_wait(inode); ++ ++ truncate_pagecache(inode, vbo_down); ++ ++ if (!is_sparsed(ni) && !is_compressed(ni)) { ++ /* normal file */ ++ err = ntfs_zero_range(inode, vbo, end); ++ goto out; ++ } ++ ++ ni_lock(ni); ++ err = attr_punch_hole(ni, vbo, len, &frame_size); ++ ni_unlock(ni); ++ if (err != E_NTFS_NOTALIGNED) ++ goto out; ++ ++ /* process not aligned punch */ ++ mask = frame_size - 1; ++ vbo_a = (vbo + mask) & ~mask; ++ end_a = end & ~mask; ++ ++ tmp = min(vbo_a, end); ++ if (tmp > vbo) { ++ err = ntfs_zero_range(inode, vbo, tmp); ++ if (err) ++ goto out; ++ } ++ ++ if (vbo < end_a && end_a < end) { ++ err = ntfs_zero_range(inode, end_a, end); ++ if (err) ++ goto out; ++ } ++ ++ /* Aligned punch_hole */ ++ if (end_a > vbo_a) { ++ ni_lock(ni); ++ err = attr_punch_hole(ni, vbo_a, end_a - vbo_a, NULL); ++ ni_unlock(ni); ++ } ++ } else if (mode & FALLOC_FL_COLLAPSE_RANGE) { ++ if (mode & ~FALLOC_FL_COLLAPSE_RANGE) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Write tail of the last page before removed range since ++ * it will get removed from the page cache below. ++ */ ++ err = filemap_write_and_wait_range(inode->i_mapping, vbo_down, ++ vbo); ++ if (err) ++ goto out; ++ ++ /* ++ * Write data that will be shifted to preserve them ++ * when discarding page cache below ++ */ ++ err = filemap_write_and_wait_range(inode->i_mapping, end, ++ LLONG_MAX); ++ if (err) ++ goto out; ++ ++ /* Wait for existing dio to complete */ ++ inode_dio_wait(inode); ++ ++ truncate_pagecache(inode, vbo_down); ++ ++ ni_lock(ni); ++ err = attr_collapse_range(ni, vbo, len); ++ ni_unlock(ni); ++ } else { ++ /* ++ * normal file: allocate clusters, do not change 'valid' size ++ */ ++ err = ntfs_set_size(inode, max(end, i_size)); ++ if (err) ++ goto out; ++ ++ if (is_sparsed(ni) || is_compressed(ni)) { ++ CLST vcn_v = ni->i_valid >> sbi->cluster_bits; ++ CLST vcn = vbo >> sbi->cluster_bits; ++ CLST cend = bytes_to_cluster(sbi, end); ++ CLST lcn, clen; ++ bool new; ++ ++ /* ++ * allocate but not zero new clusters (see below comments) ++ * this breaks security (one can read unused on-disk areas) ++ * zeroing these clusters may be too long ++ * may be we should check here for root rights? ++ */ ++ for (; vcn < cend; vcn += clen) { ++ err = attr_data_get_block(ni, vcn, cend - vcn, ++ &lcn, &clen, &new); ++ if (err) ++ goto out; ++ if (!new || vcn >= vcn_v) ++ continue; ++ ++ /* ++ * Unwritten area ++ * NTFS is not able to store several unwritten areas ++ * Activate 'ntfs_sparse_cluster' to zero new allocated clusters ++ * ++ * Dangerous in case: ++ * 1G of sparsed clusters + 1 cluster of data => ++ * valid_size == 1G + 1 cluster ++ * fallocate(1G) will zero 1G and this can be very long ++ * xfstest 016/086 will fail without 'ntfs_sparse_cluster' ++ */ ++ ntfs_sparse_cluster(inode, NULL, vcn, ++ min(vcn_v - vcn, clen)); ++ } ++ } ++ ++ if (mode & FALLOC_FL_KEEP_SIZE) { ++ ni_lock(ni); ++ /*true - keep preallocated*/ ++ err = attr_set_size(ni, ATTR_DATA, NULL, 0, ++ &ni->file.run, i_size, &ni->i_valid, ++ true, NULL); ++ ni_unlock(ni); ++ } ++ } ++ ++out: ++ if (err == -EFBIG) ++ err = -ENOSPC; ++ ++ if (!err) { ++ inode->i_ctime = inode->i_mtime = current_time(inode); ++ mark_inode_dirty(inode); ++ } ++ ++ inode_unlock(inode); ++ return err; ++} ++ ++/* ++ * inode_operations::setattr ++ */ ++int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ++ struct iattr *attr) ++{ ++ struct super_block *sb = dentry->d_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct inode *inode = d_inode(dentry); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ u32 ia_valid = attr->ia_valid; ++ umode_t mode = inode->i_mode; ++ int err; ++ ++ if (sbi->options.no_acs_rules) { ++ /* "no access rules" - force any changes of time etc. */ ++ attr->ia_valid |= ATTR_FORCE; ++ /* and disable for editing some attributes */ ++ attr->ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE); ++ ia_valid = attr->ia_valid; ++ } ++ ++ err = setattr_prepare(mnt_userns, dentry, attr); ++ if (err) ++ goto out; ++ ++ if (ia_valid & ATTR_SIZE) { ++ loff_t oldsize = inode->i_size; ++ ++ if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { ++ /* should never be here, see ntfs_file_open*/ ++ err = -EOPNOTSUPP; ++ goto out; ++ } ++ inode_dio_wait(inode); ++ ++ if (attr->ia_size < oldsize) ++ err = ntfs_truncate(inode, attr->ia_size); ++ else if (attr->ia_size > oldsize) ++ err = ntfs_extend(inode, attr->ia_size, 0, NULL); ++ ++ if (err) ++ goto out; ++ ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ } ++ ++ setattr_copy(mnt_userns, inode, attr); ++ ++ if (mode != inode->i_mode) { ++ err = ntfs_acl_chmod(mnt_userns, inode); ++ if (err) ++ goto out; ++ ++ /* linux 'w' -> windows 'ro' */ ++ if (0222 & inode->i_mode) ++ ni->std_fa &= ~FILE_ATTRIBUTE_READONLY; ++ else ++ ni->std_fa |= FILE_ATTRIBUTE_READONLY; ++ } ++ ++ if (ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE)) ++ ntfs_save_wsl_perm(inode); ++ mark_inode_dirty(inode); ++out: ++ return err; ++} ++ ++static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) ++{ ++ ssize_t err; ++ size_t count = iov_iter_count(iter); ++ struct file *file = iocb->ki_filp; ++ struct inode *inode = file->f_mapping->host; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (is_encrypted(ni)) { ++ ntfs_inode_warn(inode, "encrypted i/o not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) { ++ ntfs_inode_warn(inode, "direct i/o + compressed not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++#ifndef CONFIG_NTFS3_LZX_XPRESS ++ if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) { ++ ntfs_inode_warn( ++ inode, ++ "activate CONFIG_NTFS3_LZX_XPRESS to read external compressed files"); ++ return -EOPNOTSUPP; ++ } ++#endif ++ ++ if (is_dedup(ni)) { ++ ntfs_inode_warn(inode, "read deduplicated not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ err = count ? generic_file_read_iter(iocb, iter) : 0; ++ ++ return err; ++} ++ ++/* returns array of locked pages */ ++static int ntfs_get_frame_pages(struct address_space *mapping, pgoff_t index, ++ struct page **pages, u32 pages_per_frame, ++ bool *frame_uptodate) ++{ ++ gfp_t gfp_mask = mapping_gfp_mask(mapping); ++ u32 npages; ++ ++ *frame_uptodate = true; ++ ++ for (npages = 0; npages < pages_per_frame; npages++, index++) { ++ struct page *page; ++ ++ page = find_or_create_page(mapping, index, gfp_mask); ++ if (!page) { ++ while (npages--) { ++ page = pages[npages]; ++ unlock_page(page); ++ put_page(page); ++ } ++ ++ return -ENOMEM; ++ } ++ ++ if (!PageUptodate(page)) ++ *frame_uptodate = false; ++ ++ pages[npages] = page; ++ } ++ ++ return 0; ++} ++ ++/*helper for ntfs_file_write_iter (compressed files)*/ ++static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) ++{ ++ int err; ++ struct file *file = iocb->ki_filp; ++ size_t count = iov_iter_count(from); ++ loff_t pos = iocb->ki_pos; ++ struct inode *inode = file_inode(file); ++ loff_t i_size = inode->i_size; ++ struct address_space *mapping = inode->i_mapping; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ u64 valid = ni->i_valid; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct page *page, **pages = NULL; ++ size_t written = 0; ++ u8 frame_bits = NTFS_LZNT_CUNIT + sbi->cluster_bits; ++ u32 frame_size = 1u << frame_bits; ++ u32 pages_per_frame = frame_size >> PAGE_SHIFT; ++ u32 ip, off; ++ CLST frame; ++ u64 frame_vbo; ++ pgoff_t index; ++ bool frame_uptodate; ++ ++ if (frame_size < PAGE_SIZE) { ++ /* ++ * frame_size == 8K if cluster 512 ++ * frame_size == 64K if cluster 4096 ++ */ ++ ntfs_inode_warn(inode, "page size is bigger than frame size"); ++ return -EOPNOTSUPP; ++ } ++ ++ pages = ntfs_malloc(pages_per_frame * sizeof(struct page *)); ++ if (!pages) ++ return -ENOMEM; ++ ++ current->backing_dev_info = inode_to_bdi(inode); ++ err = file_remove_privs(file); ++ if (err) ++ goto out; ++ ++ err = file_update_time(file); ++ if (err) ++ goto out; ++ ++ /* zero range [valid : pos) */ ++ while (valid < pos) { ++ CLST lcn, clen; ++ ++ frame = valid >> frame_bits; ++ frame_vbo = valid & ~(frame_size - 1); ++ off = valid & (frame_size - 1); ++ ++ err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 0, &lcn, ++ &clen, NULL); ++ if (err) ++ goto out; ++ ++ if (lcn == SPARSE_LCN) { ++ ni->i_valid = valid = ++ frame_vbo + ((u64)clen << sbi->cluster_bits); ++ continue; ++ } ++ ++ /* Load full frame */ ++ err = ntfs_get_frame_pages(mapping, frame_vbo >> PAGE_SHIFT, ++ pages, pages_per_frame, ++ &frame_uptodate); ++ if (err) ++ goto out; ++ ++ if (!frame_uptodate && off) { ++ err = ni_read_frame(ni, frame_vbo, pages, ++ pages_per_frame); ++ if (err) { ++ for (ip = 0; ip < pages_per_frame; ip++) { ++ page = pages[ip]; ++ unlock_page(page); ++ put_page(page); ++ } ++ goto out; ++ } ++ } ++ ++ ip = off >> PAGE_SHIFT; ++ off = offset_in_page(valid); ++ for (; ip < pages_per_frame; ip++, off = 0) { ++ page = pages[ip]; ++ zero_user_segment(page, off, PAGE_SIZE); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ } ++ ++ ni_lock(ni); ++ err = ni_write_frame(ni, pages, pages_per_frame); ++ ni_unlock(ni); ++ ++ for (ip = 0; ip < pages_per_frame; ip++) { ++ page = pages[ip]; ++ SetPageUptodate(page); ++ unlock_page(page); ++ put_page(page); ++ } ++ ++ if (err) ++ goto out; ++ ++ ni->i_valid = valid = frame_vbo + frame_size; ++ } ++ ++ /* copy user data [pos : pos + count) */ ++ while (count) { ++ size_t copied, bytes; ++ ++ off = pos & (frame_size - 1); ++ bytes = frame_size - off; ++ if (bytes > count) ++ bytes = count; ++ ++ frame = pos >> frame_bits; ++ frame_vbo = pos & ~(frame_size - 1); ++ index = frame_vbo >> PAGE_SHIFT; ++ ++ if (unlikely(iov_iter_fault_in_readable(from, bytes))) { ++ err = -EFAULT; ++ goto out; ++ } ++ ++ /* Load full frame */ ++ err = ntfs_get_frame_pages(mapping, index, pages, ++ pages_per_frame, &frame_uptodate); ++ if (err) ++ goto out; ++ ++ if (!frame_uptodate) { ++ loff_t to = pos + bytes; ++ ++ if (off || (to < i_size && (to & (frame_size - 1)))) { ++ err = ni_read_frame(ni, frame_vbo, pages, ++ pages_per_frame); ++ if (err) { ++ for (ip = 0; ip < pages_per_frame; ++ ip++) { ++ page = pages[ip]; ++ unlock_page(page); ++ put_page(page); ++ } ++ goto out; ++ } ++ } ++ } ++ ++ WARN_ON(!bytes); ++ copied = 0; ++ ip = off >> PAGE_SHIFT; ++ off = offset_in_page(pos); ++ ++ /* copy user data to pages */ ++ for (;;) { ++ size_t cp, tail = PAGE_SIZE - off; ++ ++ page = pages[ip]; ++ cp = copy_page_from_iter_atomic(page, off, ++ min(tail, bytes), from); ++ flush_dcache_page(page); ++ ++ copied += cp; ++ bytes -= cp; ++ if (!bytes || !cp) ++ break; ++ ++ if (cp < tail) { ++ off += cp; ++ } else { ++ ip++; ++ off = 0; ++ } ++ } ++ ++ ni_lock(ni); ++ err = ni_write_frame(ni, pages, pages_per_frame); ++ ni_unlock(ni); ++ ++ for (ip = 0; ip < pages_per_frame; ip++) { ++ page = pages[ip]; ++ ClearPageDirty(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ put_page(page); ++ } ++ ++ if (err) ++ goto out; ++ ++ /* ++ * We can loop for a long time in here. Be nice and allow ++ * us to schedule out to avoid softlocking if preempt ++ * is disabled. ++ */ ++ cond_resched(); ++ ++ pos += copied; ++ written += copied; ++ ++ count = iov_iter_count(from); ++ } ++ ++out: ++ ntfs_free(pages); ++ ++ current->backing_dev_info = NULL; ++ ++ if (err < 0) ++ return err; ++ ++ iocb->ki_pos += written; ++ if (iocb->ki_pos > ni->i_valid) ++ ni->i_valid = iocb->ki_pos; ++ ++ return written; ++} ++ ++/* ++ * file_operations::write_iter ++ */ ++static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ++{ ++ struct file *file = iocb->ki_filp; ++ struct address_space *mapping = file->f_mapping; ++ struct inode *inode = mapping->host; ++ ssize_t ret; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (is_encrypted(ni)) { ++ ntfs_inode_warn(inode, "encrypted i/o not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (is_compressed(ni) && (iocb->ki_flags & IOCB_DIRECT)) { ++ ntfs_inode_warn(inode, "direct i/o + compressed not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (is_dedup(ni)) { ++ ntfs_inode_warn(inode, "write into deduplicated not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (!inode_trylock(inode)) { ++ if (iocb->ki_flags & IOCB_NOWAIT) ++ return -EAGAIN; ++ inode_lock(inode); ++ } ++ ++ ret = generic_write_checks(iocb, from); ++ if (ret <= 0) ++ goto out; ++ ++ if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { ++ /* should never be here, see ntfs_file_open*/ ++ ret = -EOPNOTSUPP; ++ goto out; ++ } ++ ++ ret = ntfs_extend(inode, iocb->ki_pos, ret, file); ++ if (ret) ++ goto out; ++ ++ ret = is_compressed(ni) ? ntfs_compress_write(iocb, from) ++ : __generic_file_write_iter(iocb, from); ++ ++out: ++ inode_unlock(inode); ++ ++ if (ret > 0) ++ ret = generic_write_sync(iocb, ret); ++ ++ return ret; ++} ++ ++/* ++ * file_operations::open ++ */ ++int ntfs_file_open(struct inode *inode, struct file *file) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (unlikely((is_compressed(ni) || is_encrypted(ni)) && ++ (file->f_flags & O_DIRECT))) { ++ return -EOPNOTSUPP; ++ } ++ ++ /* Decompress "external compressed" file if opened for rw */ ++ if ((ni->ni_flags & NI_FLAG_COMPRESSED_MASK) && ++ (file->f_flags & (O_WRONLY | O_RDWR | O_TRUNC))) { ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ int err = ni_decompress_file(ni); ++ ++ if (err) ++ return err; ++#else ++ ntfs_inode_warn( ++ inode, ++ "activate CONFIG_NTFS3_LZX_XPRESS to write external compressed files"); ++ return -EOPNOTSUPP; ++#endif ++ } ++ ++ return generic_file_open(inode, file); ++} ++ ++/* ++ * file_operations::release ++ */ ++static int ntfs_file_release(struct inode *inode, struct file *file) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ int err = 0; ++ ++ /* if we are the last writer on the inode, drop the block reservation */ ++ if (sbi->options.prealloc && ((file->f_mode & FMODE_WRITE) && ++ atomic_read(&inode->i_writecount) == 1)) { ++ ni_lock(ni); ++ down_write(&ni->file.run_lock); ++ ++ err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, ++ inode->i_size, &ni->i_valid, false, NULL); ++ ++ up_write(&ni->file.run_lock); ++ ni_unlock(ni); ++ } ++ return err; ++} ++ ++/* file_operations::fiemap */ ++int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ++ __u64 start, __u64 len) ++{ ++ int err; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) ++ return -EOPNOTSUPP; ++ ++ ni_lock(ni); ++ ++ err = ni_fiemap(ni, fieinfo, start, len); ++ ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++// clang-format off ++const struct inode_operations ntfs_file_inode_operations = { ++ .getattr = ntfs_getattr, ++ .setattr = ntfs3_setattr, ++ .listxattr = ntfs_listxattr, ++ .permission = ntfs_permission, ++ .get_acl = ntfs_get_acl, ++ .set_acl = ntfs_set_acl, ++ .fiemap = ntfs_fiemap, ++}; ++ ++const struct file_operations ntfs_file_operations = { ++ .llseek = generic_file_llseek, ++ .read_iter = ntfs_file_read_iter, ++ .write_iter = ntfs_file_write_iter, ++ .unlocked_ioctl = ntfs_ioctl, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = ntfs_compat_ioctl, ++#endif ++ .splice_read = generic_file_splice_read, ++ .mmap = ntfs_file_mmap, ++ .open = ntfs_file_open, ++ .fsync = generic_file_fsync, ++ .splice_write = iter_file_splice_write, ++ .fallocate = ntfs_fallocate, ++ .release = ntfs_file_release, ++}; ++// clang-format on +diff --git a/src/frecord.c b/src/frecord.c +new file mode 100644 +index 0000000000000000000000000000000000000000..c3121bf9c62fd82c27302b9337b1a5c6a4a61bef +--- /dev/null ++++ b/src/frecord.c +@@ -0,0 +1,3071 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++#include "lib/lib.h" ++#endif ++ ++static struct mft_inode *ni_ins_mi(struct ntfs_inode *ni, struct rb_root *tree, ++ CLST ino, struct rb_node *ins) ++{ ++ struct rb_node **p = &tree->rb_node; ++ struct rb_node *pr = NULL; ++ ++ while (*p) { ++ struct mft_inode *mi; ++ ++ pr = *p; ++ mi = rb_entry(pr, struct mft_inode, node); ++ if (mi->rno > ino) ++ p = &pr->rb_left; ++ else if (mi->rno < ino) ++ p = &pr->rb_right; ++ else ++ return mi; ++ } ++ ++ if (!ins) ++ return NULL; ++ ++ rb_link_node(ins, pr, p); ++ rb_insert_color(ins, tree); ++ return rb_entry(ins, struct mft_inode, node); ++} ++ ++/* ++ * ni_find_mi ++ * ++ * finds mft_inode by record number ++ */ ++static struct mft_inode *ni_find_mi(struct ntfs_inode *ni, CLST rno) ++{ ++ return ni_ins_mi(ni, &ni->mi_tree, rno, NULL); ++} ++ ++/* ++ * ni_add_mi ++ * ++ * adds new mft_inode into ntfs_inode ++ */ ++static void ni_add_mi(struct ntfs_inode *ni, struct mft_inode *mi) ++{ ++ ni_ins_mi(ni, &ni->mi_tree, mi->rno, &mi->node); ++} ++ ++/* ++ * ni_remove_mi ++ * ++ * removes mft_inode from ntfs_inode ++ */ ++void ni_remove_mi(struct ntfs_inode *ni, struct mft_inode *mi) ++{ ++ rb_erase(&mi->node, &ni->mi_tree); ++} ++ ++/* ++ * ni_std ++ * ++ * returns pointer into std_info from primary record ++ */ ++struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni) ++{ ++ const struct ATTRIB *attr; ++ ++ attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL); ++ return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO)) ++ : NULL; ++} ++ ++/* ++ * ni_std5 ++ * ++ * returns pointer into std_info from primary record ++ */ ++struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni) ++{ ++ const struct ATTRIB *attr; ++ ++ attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL); ++ ++ return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO5)) ++ : NULL; ++} ++ ++/* ++ * ni_clear ++ * ++ * clears resources allocated by ntfs_inode ++ */ ++void ni_clear(struct ntfs_inode *ni) ++{ ++ struct rb_node *node; ++ ++ if (!ni->vfs_inode.i_nlink && is_rec_inuse(ni->mi.mrec)) ++ ni_delete_all(ni); ++ ++ al_destroy(ni); ++ ++ for (node = rb_first(&ni->mi_tree); node;) { ++ struct rb_node *next = rb_next(node); ++ struct mft_inode *mi = rb_entry(node, struct mft_inode, node); ++ ++ rb_erase(node, &ni->mi_tree); ++ mi_put(mi); ++ node = next; ++ } ++ ++ /* bad inode always has mode == S_IFREG */ ++ if (ni->ni_flags & NI_FLAG_DIR) ++ indx_clear(&ni->dir); ++ else { ++ run_close(&ni->file.run); ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ if (ni->file.offs_page) { ++ /* on-demand allocated page for offsets */ ++ put_page(ni->file.offs_page); ++ ni->file.offs_page = NULL; ++ } ++#endif ++ } ++ ++ mi_clear(&ni->mi); ++} ++ ++/* ++ * ni_load_mi_ex ++ * ++ * finds mft_inode by record number. ++ */ ++int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) ++{ ++ int err; ++ struct mft_inode *r; ++ ++ r = ni_find_mi(ni, rno); ++ if (r) ++ goto out; ++ ++ err = mi_get(ni->mi.sbi, rno, &r); ++ if (err) ++ return err; ++ ++ ni_add_mi(ni, r); ++ ++out: ++ if (mi) ++ *mi = r; ++ return 0; ++} ++ ++/* ++ * ni_load_mi ++ * ++ * load mft_inode corresponded list_entry ++ */ ++int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, ++ struct mft_inode **mi) ++{ ++ CLST rno; ++ ++ if (!le) { ++ *mi = &ni->mi; ++ return 0; ++ } ++ ++ rno = ino_get(&le->ref); ++ if (rno == ni->mi.rno) { ++ *mi = &ni->mi; ++ return 0; ++ } ++ return ni_load_mi_ex(ni, rno, mi); ++} ++ ++/* ++ * ni_find_attr ++ * ++ * returns attribute and record this attribute belongs to ++ */ ++struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY **le_o, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, const CLST *vcn, ++ struct mft_inode **mi) ++{ ++ struct ATTR_LIST_ENTRY *le; ++ struct mft_inode *m; ++ ++ if (!ni->attr_list.size || ++ (!name_len && (type == ATTR_LIST || type == ATTR_STD))) { ++ if (le_o) ++ *le_o = NULL; ++ if (mi) ++ *mi = &ni->mi; ++ ++ /* Look for required attribute in primary record */ ++ return mi_find_attr(&ni->mi, attr, type, name, name_len, NULL); ++ } ++ ++ /* first look for list entry of required type */ ++ le = al_find_ex(ni, le_o ? *le_o : NULL, type, name, name_len, vcn); ++ if (!le) ++ return NULL; ++ ++ if (le_o) ++ *le_o = le; ++ ++ /* Load record that contains this attribute */ ++ if (ni_load_mi(ni, le, &m)) ++ return NULL; ++ ++ /* Look for required attribute */ ++ attr = mi_find_attr(m, NULL, type, name, name_len, &le->id); ++ ++ if (!attr) ++ goto out; ++ ++ if (!attr->non_res) { ++ if (vcn && *vcn) ++ goto out; ++ } else if (!vcn) { ++ if (attr->nres.svcn) ++ goto out; ++ } else if (le64_to_cpu(attr->nres.svcn) > *vcn || ++ *vcn > le64_to_cpu(attr->nres.evcn)) { ++ goto out; ++ } ++ ++ if (mi) ++ *mi = m; ++ return attr; ++ ++out: ++ ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR); ++ return NULL; ++} ++ ++/* ++ * ni_enum_attr_ex ++ * ++ * enumerates attributes in ntfs_inode ++ */ ++struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY **le, ++ struct mft_inode **mi) ++{ ++ struct mft_inode *mi2; ++ struct ATTR_LIST_ENTRY *le2; ++ ++ /* Do we have an attribute list? */ ++ if (!ni->attr_list.size) { ++ *le = NULL; ++ if (mi) ++ *mi = &ni->mi; ++ /* Enum attributes in primary record */ ++ return mi_enum_attr(&ni->mi, attr); ++ } ++ ++ /* get next list entry */ ++ le2 = *le = al_enumerate(ni, attr ? *le : NULL); ++ if (!le2) ++ return NULL; ++ ++ /* Load record that contains the required attribute */ ++ if (ni_load_mi(ni, le2, &mi2)) ++ return NULL; ++ ++ if (mi) ++ *mi = mi2; ++ ++ /* Find attribute in loaded record */ ++ return rec_find_attr_le(mi2, le2); ++} ++ ++/* ++ * ni_load_attr ++ * ++ * loads attribute that contains given vcn ++ */ ++struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, CLST vcn, ++ struct mft_inode **pmi) ++{ ++ struct ATTR_LIST_ENTRY *le; ++ struct ATTRIB *attr; ++ struct mft_inode *mi; ++ struct ATTR_LIST_ENTRY *next; ++ ++ if (!ni->attr_list.size) { ++ if (pmi) ++ *pmi = &ni->mi; ++ return mi_find_attr(&ni->mi, NULL, type, name, name_len, NULL); ++ } ++ ++ le = al_find_ex(ni, NULL, type, name, name_len, NULL); ++ if (!le) ++ return NULL; ++ ++ /* ++ * Unfortunately ATTR_LIST_ENTRY contains only start vcn ++ * So to find the ATTRIB segment that contains 'vcn' we should ++ * enumerate some entries ++ */ ++ if (vcn) { ++ for (;; le = next) { ++ next = al_find_ex(ni, le, type, name, name_len, NULL); ++ if (!next || le64_to_cpu(next->vcn) > vcn) ++ break; ++ } ++ } ++ ++ if (ni_load_mi(ni, le, &mi)) ++ return NULL; ++ ++ if (pmi) ++ *pmi = mi; ++ ++ attr = mi_find_attr(mi, NULL, type, name, name_len, &le->id); ++ if (!attr) ++ return NULL; ++ ++ if (!attr->non_res) ++ return attr; ++ ++ if (le64_to_cpu(attr->nres.svcn) <= vcn && ++ vcn <= le64_to_cpu(attr->nres.evcn)) ++ return attr; ++ ++ return NULL; ++} ++ ++/* ++ * ni_load_all_mi ++ * ++ * loads all subrecords ++ */ ++int ni_load_all_mi(struct ntfs_inode *ni) ++{ ++ int err; ++ struct ATTR_LIST_ENTRY *le; ++ ++ if (!ni->attr_list.size) ++ return 0; ++ ++ le = NULL; ++ ++ while ((le = al_enumerate(ni, le))) { ++ CLST rno = ino_get(&le->ref); ++ ++ if (rno == ni->mi.rno) ++ continue; ++ ++ err = ni_load_mi_ex(ni, rno, NULL); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ni_add_subrecord ++ * ++ * allocate + format + attach a new subrecord ++ */ ++bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) ++{ ++ struct mft_inode *m; ++ ++ m = ntfs_zalloc(sizeof(struct mft_inode)); ++ if (!m) ++ return false; ++ ++ if (mi_format_new(m, ni->mi.sbi, rno, 0, ni->mi.rno == MFT_REC_MFT)) { ++ mi_put(m); ++ return false; ++ } ++ ++ mi_get_ref(&ni->mi, &m->mrec->parent_ref); ++ ++ ni_add_mi(ni, m); ++ *mi = m; ++ return true; ++} ++ ++/* ++ * ni_remove_attr ++ * ++ * removes all attributes for the given type/name/id ++ */ ++int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, size_t name_len, bool base_only, ++ const __le16 *id) ++{ ++ int err; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ struct mft_inode *mi; ++ u32 type_in; ++ int diff; ++ ++ if (base_only || type == ATTR_LIST || !ni->attr_list.size) { ++ attr = mi_find_attr(&ni->mi, NULL, type, name, name_len, id); ++ if (!attr) ++ return -ENOENT; ++ ++ mi_remove_attr(&ni->mi, attr); ++ return 0; ++ } ++ ++ type_in = le32_to_cpu(type); ++ le = NULL; ++ ++ for (;;) { ++ le = al_enumerate(ni, le); ++ if (!le) ++ return 0; ++ ++next_le2: ++ diff = le32_to_cpu(le->type) - type_in; ++ if (diff < 0) ++ continue; ++ ++ if (diff > 0) ++ return 0; ++ ++ if (le->name_len != name_len) ++ continue; ++ ++ if (name_len && ++ memcmp(le_name(le), name, name_len * sizeof(short))) ++ continue; ++ ++ if (id && le->id != *id) ++ continue; ++ err = ni_load_mi(ni, le, &mi); ++ if (err) ++ return err; ++ ++ al_remove_le(ni, le); ++ ++ attr = mi_find_attr(mi, NULL, type, name, name_len, id); ++ if (!attr) ++ return -ENOENT; ++ ++ mi_remove_attr(mi, attr); ++ ++ if (PtrOffset(ni->attr_list.le, le) >= ni->attr_list.size) ++ return 0; ++ goto next_le2; ++ } ++} ++ ++/* ++ * ni_ins_new_attr ++ * ++ * inserts the attribute into record ++ * Returns not full constructed attribute or NULL if not possible to create ++ */ ++static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, ++ struct mft_inode *mi, ++ struct ATTR_LIST_ENTRY *le, ++ enum ATTR_TYPE type, const __le16 *name, ++ u8 name_len, u32 asize, u16 name_off, ++ CLST svcn) ++{ ++ int err; ++ struct ATTRIB *attr; ++ bool le_added = false; ++ struct MFT_REF ref; ++ ++ mi_get_ref(mi, &ref); ++ ++ if (type != ATTR_LIST && !le && ni->attr_list.size) { ++ err = al_add_le(ni, type, name, name_len, svcn, cpu_to_le16(-1), ++ &ref, &le); ++ if (err) { ++ /* no memory or no space */ ++ return NULL; ++ } ++ le_added = true; ++ ++ /* ++ * al_add_le -> attr_set_size (list) -> ni_expand_list ++ * which moves some attributes out of primary record ++ * this means that name may point into moved memory ++ * reinit 'name' from le ++ */ ++ name = le->name; ++ } ++ ++ attr = mi_insert_attr(mi, type, name, name_len, asize, name_off); ++ if (!attr) { ++ if (le_added) ++ al_remove_le(ni, le); ++ return NULL; ++ } ++ ++ if (type == ATTR_LIST) { ++ /*attr list is not in list entry array*/ ++ goto out; ++ } ++ ++ if (!le) ++ goto out; ++ ++ /* Update ATTRIB Id and record reference */ ++ le->id = attr->id; ++ ni->attr_list.dirty = true; ++ le->ref = ref; ++ ++out: ++ return attr; ++} ++ ++/* ++ * random write access to sparsed or compressed file may result to ++ * not optimized packed runs. ++ * Here it is the place to optimize it ++ */ ++static int ni_repack(struct ntfs_inode *ni) ++{ ++ int err = 0; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct mft_inode *mi, *mi_p = NULL; ++ struct ATTRIB *attr = NULL, *attr_p; ++ struct ATTR_LIST_ENTRY *le = NULL, *le_p; ++ CLST alloc = 0; ++ u8 cluster_bits = sbi->cluster_bits; ++ CLST svcn, evcn = 0, svcn_p, evcn_p, next_svcn; ++ u32 roff, rs = sbi->record_size; ++ struct runs_tree run; ++ ++ run_init(&run); ++ ++ while ((attr = ni_enum_attr_ex(ni, attr, &le, &mi))) { ++ if (!attr->non_res) ++ continue; ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ if (svcn != le64_to_cpu(le->vcn)) { ++ err = -EINVAL; ++ break; ++ } ++ ++ if (!svcn) { ++ alloc = le64_to_cpu(attr->nres.alloc_size) >> ++ cluster_bits; ++ mi_p = NULL; ++ } else if (svcn != evcn + 1) { ++ err = -EINVAL; ++ break; ++ } ++ ++ evcn = le64_to_cpu(attr->nres.evcn); ++ ++ if (svcn > evcn + 1) { ++ err = -EINVAL; ++ break; ++ } ++ ++ if (!mi_p) { ++ /* do not try if too little free space */ ++ if (le32_to_cpu(mi->mrec->used) + 8 >= rs) ++ continue; ++ ++ /* do not try if last attribute segment */ ++ if (evcn + 1 == alloc) ++ continue; ++ run_close(&run); ++ } ++ ++ roff = le16_to_cpu(attr->nres.run_off); ++ err = run_unpack(&run, sbi, ni->mi.rno, svcn, evcn, svcn, ++ Add2Ptr(attr, roff), ++ le32_to_cpu(attr->size) - roff); ++ if (err < 0) ++ break; ++ ++ if (!mi_p) { ++ mi_p = mi; ++ attr_p = attr; ++ svcn_p = svcn; ++ evcn_p = evcn; ++ le_p = le; ++ err = 0; ++ continue; ++ } ++ ++ /* ++ * run contains data from two records: mi_p and mi ++ * try to pack in one ++ */ ++ err = mi_pack_runs(mi_p, attr_p, &run, evcn + 1 - svcn_p); ++ if (err) ++ break; ++ ++ next_svcn = le64_to_cpu(attr_p->nres.evcn) + 1; ++ ++ if (next_svcn >= evcn + 1) { ++ /* we can remove this attribute segment */ ++ al_remove_le(ni, le); ++ mi_remove_attr(mi, attr); ++ le = le_p; ++ continue; ++ } ++ ++ attr->nres.svcn = le->vcn = cpu_to_le64(next_svcn); ++ mi->dirty = true; ++ ni->attr_list.dirty = true; ++ ++ if (evcn + 1 == alloc) { ++ err = mi_pack_runs(mi, attr, &run, ++ evcn + 1 - next_svcn); ++ if (err) ++ break; ++ mi_p = NULL; ++ } else { ++ mi_p = mi; ++ attr_p = attr; ++ svcn_p = next_svcn; ++ evcn_p = evcn; ++ le_p = le; ++ run_truncate_head(&run, next_svcn); ++ } ++ } ++ ++ if (err) { ++ ntfs_inode_warn(&ni->vfs_inode, "repack problem"); ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ ++ /* Pack loaded but not packed runs */ ++ if (mi_p) ++ mi_pack_runs(mi_p, attr_p, &run, evcn_p + 1 - svcn_p); ++ } ++ ++ run_close(&run); ++ return err; ++} ++ ++/* ++ * ni_try_remove_attr_list ++ * ++ * Can we remove attribute list? ++ * Check the case when primary record contains enough space for all attributes ++ */ ++static int ni_try_remove_attr_list(struct ntfs_inode *ni) ++{ ++ int err = 0; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTRIB *attr, *attr_list, *attr_ins; ++ struct ATTR_LIST_ENTRY *le; ++ struct mft_inode *mi; ++ u32 asize, free; ++ struct MFT_REF ref; ++ __le16 id; ++ ++ if (!ni->attr_list.dirty) ++ return 0; ++ ++ err = ni_repack(ni); ++ if (err) ++ return err; ++ ++ attr_list = mi_find_attr(&ni->mi, NULL, ATTR_LIST, NULL, 0, NULL); ++ if (!attr_list) ++ return 0; ++ ++ asize = le32_to_cpu(attr_list->size); ++ ++ /* free space in primary record without attribute list */ ++ free = sbi->record_size - le32_to_cpu(ni->mi.mrec->used) + asize; ++ mi_get_ref(&ni->mi, &ref); ++ ++ le = NULL; ++ while ((le = al_enumerate(ni, le))) { ++ if (!memcmp(&le->ref, &ref, sizeof(ref))) ++ continue; ++ ++ if (le->vcn) ++ return 0; ++ ++ mi = ni_find_mi(ni, ino_get(&le->ref)); ++ if (!mi) ++ return 0; ++ ++ attr = mi_find_attr(mi, NULL, le->type, le_name(le), ++ le->name_len, &le->id); ++ if (!attr) ++ return 0; ++ ++ asize = le32_to_cpu(attr->size); ++ if (asize > free) ++ return 0; ++ ++ free -= asize; ++ } ++ ++ /* Is seems that attribute list can be removed from primary record */ ++ mi_remove_attr(&ni->mi, attr_list); ++ ++ /* ++ * Repeat the cycle above and move all attributes to primary record. ++ * It should be success! ++ */ ++ le = NULL; ++ while ((le = al_enumerate(ni, le))) { ++ if (!memcmp(&le->ref, &ref, sizeof(ref))) ++ continue; ++ ++ mi = ni_find_mi(ni, ino_get(&le->ref)); ++ ++ attr = mi_find_attr(mi, NULL, le->type, le_name(le), ++ le->name_len, &le->id); ++ asize = le32_to_cpu(attr->size); ++ ++ /* insert into primary record */ ++ attr_ins = mi_insert_attr(&ni->mi, le->type, le_name(le), ++ le->name_len, asize, ++ le16_to_cpu(attr->name_off)); ++ id = attr_ins->id; ++ ++ /* copy all except id */ ++ memcpy(attr_ins, attr, asize); ++ attr_ins->id = id; ++ ++ /* remove from original record */ ++ mi_remove_attr(mi, attr); ++ } ++ ++ run_deallocate(sbi, &ni->attr_list.run, true); ++ run_close(&ni->attr_list.run); ++ ni->attr_list.size = 0; ++ ntfs_free(ni->attr_list.le); ++ ni->attr_list.le = NULL; ++ ni->attr_list.dirty = false; ++ ++ return 0; ++} ++ ++/* ++ * ni_create_attr_list ++ * ++ * generates an attribute list for this primary record ++ */ ++int ni_create_attr_list(struct ntfs_inode *ni) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ int err; ++ u32 lsize; ++ struct ATTRIB *attr; ++ struct ATTRIB *arr_move[7]; ++ struct ATTR_LIST_ENTRY *le, *le_b[7]; ++ struct MFT_REC *rec; ++ bool is_mft; ++ CLST rno = 0; ++ struct mft_inode *mi; ++ u32 free_b, nb, to_free, rs; ++ u16 sz; ++ ++ is_mft = ni->mi.rno == MFT_REC_MFT; ++ rec = ni->mi.mrec; ++ rs = sbi->record_size; ++ ++ /* ++ * Skip estimating exact memory requirement ++ * Looks like one record_size is always enough ++ */ ++ le = ntfs_malloc(al_aligned(rs)); ++ if (!le) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ mi_get_ref(&ni->mi, &le->ref); ++ ni->attr_list.le = le; ++ ++ attr = NULL; ++ nb = 0; ++ free_b = 0; ++ attr = NULL; ++ ++ for (; (attr = mi_enum_attr(&ni->mi, attr)); le = Add2Ptr(le, sz)) { ++ sz = le_size(attr->name_len); ++ le->type = attr->type; ++ le->size = cpu_to_le16(sz); ++ le->name_len = attr->name_len; ++ le->name_off = offsetof(struct ATTR_LIST_ENTRY, name); ++ le->vcn = 0; ++ if (le != ni->attr_list.le) ++ le->ref = ni->attr_list.le->ref; ++ le->id = attr->id; ++ ++ if (attr->name_len) ++ memcpy(le->name, attr_name(attr), ++ sizeof(short) * attr->name_len); ++ else if (attr->type == ATTR_STD) ++ continue; ++ else if (attr->type == ATTR_LIST) ++ continue; ++ else if (is_mft && attr->type == ATTR_DATA) ++ continue; ++ ++ if (!nb || nb < ARRAY_SIZE(arr_move)) { ++ le_b[nb] = le; ++ arr_move[nb++] = attr; ++ free_b += le32_to_cpu(attr->size); ++ } ++ } ++ ++ lsize = PtrOffset(ni->attr_list.le, le); ++ ni->attr_list.size = lsize; ++ ++ to_free = le32_to_cpu(rec->used) + lsize + SIZEOF_RESIDENT; ++ if (to_free <= rs) { ++ to_free = 0; ++ } else { ++ to_free -= rs; ++ ++ if (to_free > free_b) { ++ err = -EINVAL; ++ goto out1; ++ } ++ } ++ ++ /* Allocate child mft. */ ++ err = ntfs_look_free_mft(sbi, &rno, is_mft, ni, &mi); ++ if (err) ++ goto out1; ++ ++ /* Call 'mi_remove_attr' in reverse order to keep pointers 'arr_move' valid */ ++ while (to_free > 0) { ++ struct ATTRIB *b = arr_move[--nb]; ++ u32 asize = le32_to_cpu(b->size); ++ u16 name_off = le16_to_cpu(b->name_off); ++ ++ attr = mi_insert_attr(mi, b->type, Add2Ptr(b, name_off), ++ b->name_len, asize, name_off); ++ WARN_ON(!attr); ++ ++ mi_get_ref(mi, &le_b[nb]->ref); ++ le_b[nb]->id = attr->id; ++ ++ /* copy all except id */ ++ memcpy(attr, b, asize); ++ attr->id = le_b[nb]->id; ++ ++ WARN_ON(!mi_remove_attr(&ni->mi, b)); ++ ++ if (to_free <= asize) ++ break; ++ to_free -= asize; ++ WARN_ON(!nb); ++ } ++ ++ attr = mi_insert_attr(&ni->mi, ATTR_LIST, NULL, 0, ++ lsize + SIZEOF_RESIDENT, SIZEOF_RESIDENT); ++ WARN_ON(!attr); ++ ++ attr->non_res = 0; ++ attr->flags = 0; ++ attr->res.data_size = cpu_to_le32(lsize); ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ attr->res.flags = 0; ++ attr->res.res = 0; ++ ++ memcpy(resident_data_ex(attr, lsize), ni->attr_list.le, lsize); ++ ++ ni->attr_list.dirty = false; ++ ++ mark_inode_dirty(&ni->vfs_inode); ++ goto out; ++ ++out1: ++ ntfs_free(ni->attr_list.le); ++ ni->attr_list.le = NULL; ++ ni->attr_list.size = 0; ++ ++out: ++ return err; ++} ++ ++/* ++ * ni_ins_attr_ext ++ * ++ * This method adds an external attribute to the ntfs_inode. ++ */ ++static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, ++ enum ATTR_TYPE type, const __le16 *name, u8 name_len, ++ u32 asize, CLST svcn, u16 name_off, bool force_ext, ++ struct ATTRIB **ins_attr, struct mft_inode **ins_mi) ++{ ++ struct ATTRIB *attr; ++ struct mft_inode *mi; ++ CLST rno; ++ u64 vbo; ++ struct rb_node *node; ++ int err; ++ bool is_mft, is_mft_data; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ ++ is_mft = ni->mi.rno == MFT_REC_MFT; ++ is_mft_data = is_mft && type == ATTR_DATA && !name_len; ++ ++ if (asize > sbi->max_bytes_per_attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * standard information and attr_list cannot be made external. ++ * The Log File cannot have any external attributes ++ */ ++ if (type == ATTR_STD || type == ATTR_LIST || ++ ni->mi.rno == MFT_REC_LOG) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Create attribute list if it is not already existed */ ++ if (!ni->attr_list.size) { ++ err = ni_create_attr_list(ni); ++ if (err) ++ goto out; ++ } ++ ++ vbo = is_mft_data ? ((u64)svcn << sbi->cluster_bits) : 0; ++ ++ if (force_ext) ++ goto insert_ext; ++ ++ /* Load all subrecords into memory. */ ++ err = ni_load_all_mi(ni); ++ if (err) ++ goto out; ++ ++ /* Check each of loaded subrecord */ ++ for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) { ++ mi = rb_entry(node, struct mft_inode, node); ++ ++ if (is_mft_data && ++ (mi_enum_attr(mi, NULL) || ++ vbo <= ((u64)mi->rno << sbi->record_bits))) { ++ /* We can't accept this record 'case MFT's bootstrapping */ ++ continue; ++ } ++ if (is_mft && ++ mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, NULL)) { ++ /* ++ * This child record already has a ATTR_DATA. ++ * So it can't accept any other records. ++ */ ++ continue; ++ } ++ ++ if ((type != ATTR_NAME || name_len) && ++ mi_find_attr(mi, NULL, type, name, name_len, NULL)) { ++ /* Only indexed attributes can share same record */ ++ continue; ++ } ++ ++ /* Try to insert attribute into this subrecord */ ++ attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize, ++ name_off, svcn); ++ if (!attr) ++ continue; ++ ++ if (ins_attr) ++ *ins_attr = attr; ++ return 0; ++ } ++ ++insert_ext: ++ /* We have to allocate a new child subrecord*/ ++ err = ntfs_look_free_mft(sbi, &rno, is_mft_data, ni, &mi); ++ if (err) ++ goto out; ++ ++ if (is_mft_data && vbo <= ((u64)rno << sbi->record_bits)) { ++ err = -EINVAL; ++ goto out1; ++ } ++ ++ attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize, ++ name_off, svcn); ++ if (!attr) ++ goto out2; ++ ++ if (ins_attr) ++ *ins_attr = attr; ++ if (ins_mi) ++ *ins_mi = mi; ++ ++ return 0; ++ ++out2: ++ ni_remove_mi(ni, mi); ++ mi_put(mi); ++ err = -EINVAL; ++ ++out1: ++ ntfs_mark_rec_free(sbi, rno); ++ ++out: ++ return err; ++} ++ ++/* ++ * ni_insert_attr ++ * ++ * inserts an attribute into the file. ++ * ++ * If the primary record has room, it will just insert the attribute. ++ * If not, it may make the attribute external. ++ * For $MFT::Data it may make room for the attribute by ++ * making other attributes external. ++ * ++ * NOTE: ++ * The ATTR_LIST and ATTR_STD cannot be made external. ++ * This function does not fill new attribute full ++ * It only fills 'size'/'type'/'id'/'name_len' fields ++ */ ++static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, u32 asize, ++ u16 name_off, CLST svcn, struct ATTRIB **ins_attr, ++ struct mft_inode **ins_mi) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ int err; ++ struct ATTRIB *attr, *eattr; ++ struct MFT_REC *rec; ++ bool is_mft; ++ struct ATTR_LIST_ENTRY *le; ++ u32 list_reserve, max_free, free, used, t32; ++ __le16 id; ++ u16 t16; ++ ++ is_mft = ni->mi.rno == MFT_REC_MFT; ++ rec = ni->mi.mrec; ++ ++ list_reserve = SIZEOF_NONRESIDENT + 3 * (1 + 2 * sizeof(u32)); ++ used = le32_to_cpu(rec->used); ++ free = sbi->record_size - used; ++ ++ if (is_mft && type != ATTR_LIST) { ++ /* Reserve space for the ATTRIB List. */ ++ if (free < list_reserve) ++ free = 0; ++ else ++ free -= list_reserve; ++ } ++ ++ if (asize <= free) { ++ attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len, ++ asize, name_off, svcn); ++ if (attr) { ++ if (ins_attr) ++ *ins_attr = attr; ++ if (ins_mi) ++ *ins_mi = &ni->mi; ++ err = 0; ++ goto out; ++ } ++ } ++ ++ if (!is_mft || type != ATTR_DATA || svcn) { ++ /* This ATTRIB will be external. */ ++ err = ni_ins_attr_ext(ni, NULL, type, name, name_len, asize, ++ svcn, name_off, false, ins_attr, ins_mi); ++ goto out; ++ } ++ ++ /* ++ * Here we have: "is_mft && type == ATTR_DATA && !svcn ++ * ++ * The first chunk of the $MFT::Data ATTRIB must be the base record. ++ * Evict as many other attributes as possible. ++ */ ++ max_free = free; ++ ++ /* Estimate the result of moving all possible attributes away.*/ ++ attr = NULL; ++ ++ while ((attr = mi_enum_attr(&ni->mi, attr))) { ++ if (attr->type == ATTR_STD) ++ continue; ++ if (attr->type == ATTR_LIST) ++ continue; ++ max_free += le32_to_cpu(attr->size); ++ } ++ ++ if (max_free < asize + list_reserve) { ++ /* Impossible to insert this attribute into primary record */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Start real attribute moving */ ++ attr = NULL; ++ ++ for (;;) { ++ attr = mi_enum_attr(&ni->mi, attr); ++ if (!attr) { ++ /* We should never be here 'cause we have already check this case */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Skip attributes that MUST be primary record */ ++ if (attr->type == ATTR_STD || attr->type == ATTR_LIST) ++ continue; ++ ++ le = NULL; ++ if (ni->attr_list.size) { ++ le = al_find_le(ni, NULL, attr); ++ if (!le) { ++ /* Really this is a serious bug */ ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ t32 = le32_to_cpu(attr->size); ++ t16 = le16_to_cpu(attr->name_off); ++ err = ni_ins_attr_ext(ni, le, attr->type, Add2Ptr(attr, t16), ++ attr->name_len, t32, attr_svcn(attr), t16, ++ false, &eattr, NULL); ++ if (err) ++ return err; ++ ++ id = eattr->id; ++ memcpy(eattr, attr, t32); ++ eattr->id = id; ++ ++ /* remove attrib from primary record */ ++ mi_remove_attr(&ni->mi, attr); ++ ++ /* attr now points to next attribute */ ++ if (attr->type == ATTR_END) ++ goto out; ++ } ++ while (asize + list_reserve > sbi->record_size - le32_to_cpu(rec->used)) ++ ; ++ ++ attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len, asize, ++ name_off, svcn); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (ins_attr) ++ *ins_attr = attr; ++ if (ins_mi) ++ *ins_mi = &ni->mi; ++ ++out: ++ return err; ++} ++ ++/* ++ * ni_expand_mft_list ++ * ++ * This method splits ATTR_DATA of $MFT ++ */ ++static int ni_expand_mft_list(struct ntfs_inode *ni) ++{ ++ int err = 0; ++ struct runs_tree *run = &ni->file.run; ++ u32 asize, run_size, done = 0; ++ struct ATTRIB *attr; ++ struct rb_node *node; ++ CLST mft_min, mft_new, svcn, evcn, plen; ++ struct mft_inode *mi, *mi_min, *mi_new; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ ++ /* Find the nearest Mft */ ++ mft_min = 0; ++ mft_new = 0; ++ mi_min = NULL; ++ ++ for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) { ++ mi = rb_entry(node, struct mft_inode, node); ++ ++ attr = mi_enum_attr(mi, NULL); ++ ++ if (!attr) { ++ mft_min = mi->rno; ++ mi_min = mi; ++ break; ++ } ++ } ++ ++ if (ntfs_look_free_mft(sbi, &mft_new, true, ni, &mi_new)) { ++ mft_new = 0; ++ // really this is not critical ++ } else if (mft_min > mft_new) { ++ mft_min = mft_new; ++ mi_min = mi_new; ++ } else { ++ ntfs_mark_rec_free(sbi, mft_new); ++ mft_new = 0; ++ ni_remove_mi(ni, mi_new); ++ } ++ ++ attr = mi_find_attr(&ni->mi, NULL, ATTR_DATA, NULL, 0, NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ asize = le32_to_cpu(attr->size); ++ ++ evcn = le64_to_cpu(attr->nres.evcn); ++ svcn = bytes_to_cluster(sbi, (u64)(mft_min + 1) << sbi->record_bits); ++ if (evcn + 1 >= svcn) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * split primary attribute [0 evcn] in two parts [0 svcn) + [svcn evcn] ++ * ++ * Update first part of ATTR_DATA in 'primary MFT ++ */ ++ err = run_pack(run, 0, svcn, Add2Ptr(attr, SIZEOF_NONRESIDENT), ++ asize - SIZEOF_NONRESIDENT, &plen); ++ if (err < 0) ++ goto out; ++ ++ run_size = QuadAlign(err); ++ err = 0; ++ ++ if (plen < svcn) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ attr->nres.evcn = cpu_to_le64(svcn - 1); ++ attr->size = cpu_to_le32(run_size + SIZEOF_NONRESIDENT); ++ /* 'done' - how many bytes of primary MFT becomes free */ ++ done = asize - run_size - SIZEOF_NONRESIDENT; ++ le32_sub_cpu(&ni->mi.mrec->used, done); ++ ++ /* Estimate the size of second part: run_buf=NULL */ ++ err = run_pack(run, svcn, evcn + 1 - svcn, NULL, sbi->record_size, ++ &plen); ++ if (err < 0) ++ goto out; ++ ++ run_size = QuadAlign(err); ++ err = 0; ++ ++ if (plen < evcn + 1 - svcn) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * This function may implicitly call expand attr_list ++ * Insert second part of ATTR_DATA in 'mi_min' ++ */ ++ attr = ni_ins_new_attr(ni, mi_min, NULL, ATTR_DATA, NULL, 0, ++ SIZEOF_NONRESIDENT + run_size, ++ SIZEOF_NONRESIDENT, svcn); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ attr->non_res = 1; ++ attr->name_off = SIZEOF_NONRESIDENT_LE; ++ attr->flags = 0; ++ ++ run_pack(run, svcn, evcn + 1 - svcn, Add2Ptr(attr, SIZEOF_NONRESIDENT), ++ run_size, &plen); ++ ++ attr->nres.svcn = cpu_to_le64(svcn); ++ attr->nres.evcn = cpu_to_le64(evcn); ++ attr->nres.run_off = cpu_to_le16(SIZEOF_NONRESIDENT); ++ ++out: ++ if (mft_new) { ++ ntfs_mark_rec_free(sbi, mft_new); ++ ni_remove_mi(ni, mi_new); ++ } ++ ++ return !err && !done ? -EOPNOTSUPP : err; ++} ++ ++/* ++ * ni_expand_list ++ * ++ * This method moves all possible attributes out of primary record ++ */ ++int ni_expand_list(struct ntfs_inode *ni) ++{ ++ int err = 0; ++ u32 asize, done = 0; ++ struct ATTRIB *attr, *ins_attr; ++ struct ATTR_LIST_ENTRY *le; ++ bool is_mft = ni->mi.rno == MFT_REC_MFT; ++ struct MFT_REF ref; ++ ++ mi_get_ref(&ni->mi, &ref); ++ le = NULL; ++ ++ while ((le = al_enumerate(ni, le))) { ++ if (le->type == ATTR_STD) ++ continue; ++ ++ if (memcmp(&ref, &le->ref, sizeof(struct MFT_REF))) ++ continue; ++ ++ if (is_mft && le->type == ATTR_DATA) ++ continue; ++ ++ /* Find attribute in primary record */ ++ attr = rec_find_attr_le(&ni->mi, le); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ asize = le32_to_cpu(attr->size); ++ ++ /* Always insert into new record to avoid collisions (deep recursive) */ ++ err = ni_ins_attr_ext(ni, le, attr->type, attr_name(attr), ++ attr->name_len, asize, attr_svcn(attr), ++ le16_to_cpu(attr->name_off), true, ++ &ins_attr, NULL); ++ ++ if (err) ++ goto out; ++ ++ memcpy(ins_attr, attr, asize); ++ ins_attr->id = le->id; ++ mi_remove_attr(&ni->mi, attr); ++ ++ done += asize; ++ goto out; ++ } ++ ++ if (!is_mft) { ++ err = -EFBIG; /* attr list is too big(?) */ ++ goto out; ++ } ++ ++ /* split mft data as much as possible */ ++ err = ni_expand_mft_list(ni); ++ if (err) ++ goto out; ++ ++out: ++ return !err && !done ? -EOPNOTSUPP : err; ++} ++ ++/* ++ * ni_insert_nonresident ++ * ++ * inserts new nonresident attribute ++ */ ++int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, ++ const struct runs_tree *run, CLST svcn, CLST len, ++ __le16 flags, struct ATTRIB **new_attr, ++ struct mft_inode **mi) ++{ ++ int err; ++ CLST plen; ++ struct ATTRIB *attr; ++ bool is_ext = ++ (flags & (ATTR_FLAG_SPARSED | ATTR_FLAG_COMPRESSED)) && !svcn; ++ u32 name_size = QuadAlign(name_len * sizeof(short)); ++ u32 name_off = is_ext ? SIZEOF_NONRESIDENT_EX : SIZEOF_NONRESIDENT; ++ u32 run_off = name_off + name_size; ++ u32 run_size, asize; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ ++ err = run_pack(run, svcn, len, NULL, sbi->max_bytes_per_attr - run_off, ++ &plen); ++ if (err < 0) ++ goto out; ++ ++ run_size = QuadAlign(err); ++ ++ if (plen < len) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ asize = run_off + run_size; ++ ++ if (asize > sbi->max_bytes_per_attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = ni_insert_attr(ni, type, name, name_len, asize, name_off, svcn, ++ &attr, mi); ++ ++ if (err) ++ goto out; ++ ++ attr->non_res = 1; ++ attr->name_off = cpu_to_le16(name_off); ++ attr->flags = flags; ++ ++ run_pack(run, svcn, len, Add2Ptr(attr, run_off), run_size, &plen); ++ ++ attr->nres.svcn = cpu_to_le64(svcn); ++ attr->nres.evcn = cpu_to_le64((u64)svcn + len - 1); ++ ++ err = 0; ++ if (new_attr) ++ *new_attr = attr; ++ ++ *(__le64 *)&attr->nres.run_off = cpu_to_le64(run_off); ++ ++ attr->nres.alloc_size = ++ svcn ? 0 : cpu_to_le64((u64)len << ni->mi.sbi->cluster_bits); ++ attr->nres.data_size = attr->nres.alloc_size; ++ attr->nres.valid_size = attr->nres.alloc_size; ++ ++ if (is_ext) { ++ if (flags & ATTR_FLAG_COMPRESSED) ++ attr->nres.c_unit = COMPRESSION_UNIT; ++ attr->nres.total_size = attr->nres.alloc_size; ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * ni_insert_resident ++ * ++ * inserts new resident attribute ++ */ ++int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, ++ enum ATTR_TYPE type, const __le16 *name, u8 name_len, ++ struct ATTRIB **new_attr, struct mft_inode **mi) ++{ ++ int err; ++ u32 name_size = QuadAlign(name_len * sizeof(short)); ++ u32 asize = SIZEOF_RESIDENT + name_size + QuadAlign(data_size); ++ struct ATTRIB *attr; ++ ++ err = ni_insert_attr(ni, type, name, name_len, asize, SIZEOF_RESIDENT, ++ 0, &attr, mi); ++ if (err) ++ return err; ++ ++ attr->non_res = 0; ++ attr->flags = 0; ++ ++ attr->res.data_size = cpu_to_le32(data_size); ++ attr->res.data_off = cpu_to_le16(SIZEOF_RESIDENT + name_size); ++ if (type == ATTR_NAME) ++ attr->res.flags = RESIDENT_FLAG_INDEXED; ++ attr->res.res = 0; ++ ++ if (new_attr) ++ *new_attr = attr; ++ ++ return 0; ++} ++ ++/* ++ * ni_remove_attr_le ++ * ++ * removes attribute from record ++ */ ++int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY *le) ++{ ++ int err; ++ struct mft_inode *mi; ++ ++ err = ni_load_mi(ni, le, &mi); ++ if (err) ++ return err; ++ ++ mi_remove_attr(mi, attr); ++ ++ if (le) ++ al_remove_le(ni, le); ++ ++ return 0; ++} ++ ++/* ++ * ni_delete_all ++ * ++ * removes all attributes and frees allocates space ++ * ntfs_evict_inode->ntfs_clear_inode->ni_delete_all (if no links) ++ */ ++int ni_delete_all(struct ntfs_inode *ni) ++{ ++ int err; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct ATTRIB *attr = NULL; ++ struct rb_node *node; ++ u16 roff; ++ u32 asize; ++ CLST svcn, evcn; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ bool nt3 = is_ntfs3(sbi); ++ struct MFT_REF ref; ++ ++ while ((attr = ni_enum_attr_ex(ni, attr, &le, NULL))) { ++ if (!nt3 || attr->name_len) { ++ ; ++ } else if (attr->type == ATTR_REPARSE) { ++ mi_get_ref(&ni->mi, &ref); ++ ntfs_remove_reparse(sbi, 0, &ref); ++ } else if (attr->type == ATTR_ID && !attr->non_res && ++ le32_to_cpu(attr->res.data_size) >= ++ sizeof(struct GUID)) { ++ ntfs_objid_remove(sbi, resident_data(attr)); ++ } ++ ++ if (!attr->non_res) ++ continue; ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ ++ if (evcn + 1 <= svcn) ++ continue; ++ ++ asize = le32_to_cpu(attr->size); ++ roff = le16_to_cpu(attr->nres.run_off); ++ ++ /*run==1 means unpack and deallocate*/ ++ run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn, ++ Add2Ptr(attr, roff), asize - roff); ++ } ++ ++ if (ni->attr_list.size) { ++ run_deallocate(ni->mi.sbi, &ni->attr_list.run, true); ++ al_destroy(ni); ++ } ++ ++ /* Free all subrecords */ ++ for (node = rb_first(&ni->mi_tree); node;) { ++ struct rb_node *next = rb_next(node); ++ struct mft_inode *mi = rb_entry(node, struct mft_inode, node); ++ ++ clear_rec_inuse(mi->mrec); ++ mi->dirty = true; ++ mi_write(mi, 0); ++ ++ ntfs_mark_rec_free(sbi, mi->rno); ++ ni_remove_mi(ni, mi); ++ mi_put(mi); ++ node = next; ++ } ++ ++ // Free base record ++ clear_rec_inuse(ni->mi.mrec); ++ ni->mi.dirty = true; ++ err = mi_write(&ni->mi, 0); ++ ++ ntfs_mark_rec_free(sbi, ni->mi.rno); ++ ++ return err; ++} ++ ++/* ++ * ni_fname_name ++ * ++ * returns file name attribute by its value ++ */ ++struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, ++ const struct cpu_str *uni, ++ const struct MFT_REF *home_dir, ++ struct ATTR_LIST_ENTRY **le) ++{ ++ struct ATTRIB *attr = NULL; ++ struct ATTR_FILE_NAME *fname; ++ ++ *le = NULL; ++ ++ /* Enumerate all names */ ++next: ++ attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, NULL); ++ if (!attr) ++ return NULL; ++ ++ fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); ++ if (!fname) ++ goto next; ++ ++ if (home_dir && memcmp(home_dir, &fname->home, sizeof(*home_dir))) ++ goto next; ++ ++ if (!uni) ++ goto next; ++ ++ if (uni->len != fname->name_len) ++ goto next; ++ ++ if (ntfs_cmp_names_cpu(uni, (struct le_str *)&fname->name_len, NULL, ++ false)) ++ goto next; ++ ++ return fname; ++} ++ ++/* ++ * ni_fname_type ++ * ++ * returns file name attribute with given type ++ */ ++struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, ++ struct ATTR_LIST_ENTRY **le) ++{ ++ struct ATTRIB *attr = NULL; ++ struct ATTR_FILE_NAME *fname; ++ ++ *le = NULL; ++ ++ /* Enumerate all names */ ++ for (;;) { ++ attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, ++ NULL); ++ if (!attr) ++ return NULL; ++ ++ fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); ++ if (fname && name_type == fname->type) ++ return fname; ++ } ++} ++ ++/* ++ * Process compressed/sparsed in special way ++ * NOTE: you need to set ni->std_fa = new_fa ++ * after this function to keep internal structures in consistency ++ */ ++int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) ++{ ++ struct ATTRIB *attr; ++ struct mft_inode *mi; ++ __le16 new_aflags; ++ u32 new_asize; ++ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi); ++ if (!attr) ++ return -EINVAL; ++ ++ new_aflags = attr->flags; ++ ++ if (new_fa & FILE_ATTRIBUTE_SPARSE_FILE) ++ new_aflags |= ATTR_FLAG_SPARSED; ++ else ++ new_aflags &= ~ATTR_FLAG_SPARSED; ++ ++ if (new_fa & FILE_ATTRIBUTE_COMPRESSED) ++ new_aflags |= ATTR_FLAG_COMPRESSED; ++ else ++ new_aflags &= ~ATTR_FLAG_COMPRESSED; ++ ++ if (new_aflags == attr->flags) ++ return 0; ++ ++ if ((new_aflags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) == ++ (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) { ++ ntfs_inode_warn(&ni->vfs_inode, ++ "file can't be sparsed and compressed"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (!attr->non_res) ++ goto out; ++ ++ if (attr->nres.data_size) { ++ ntfs_inode_warn( ++ &ni->vfs_inode, ++ "one can change sparsed/compressed only for empty files"); ++ return -EOPNOTSUPP; ++ } ++ ++ /* resize nonresident empty attribute in-place only*/ ++ new_asize = (new_aflags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) ++ ? (SIZEOF_NONRESIDENT_EX + 8) ++ : (SIZEOF_NONRESIDENT + 8); ++ ++ if (!mi_resize_attr(mi, attr, new_asize - le32_to_cpu(attr->size))) ++ return -EOPNOTSUPP; ++ ++ if (new_aflags & ATTR_FLAG_SPARSED) { ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ /* windows uses 16 clusters per frame but supports one cluster per frame too*/ ++ attr->nres.c_unit = 0; ++ ni->vfs_inode.i_mapping->a_ops = &ntfs_aops; ++ } else if (new_aflags & ATTR_FLAG_COMPRESSED) { ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ /* the only allowed: 16 clusters per frame */ ++ attr->nres.c_unit = NTFS_LZNT_CUNIT; ++ ni->vfs_inode.i_mapping->a_ops = &ntfs_aops_cmpr; ++ } else { ++ attr->name_off = SIZEOF_NONRESIDENT_LE; ++ /* normal files */ ++ attr->nres.c_unit = 0; ++ ni->vfs_inode.i_mapping->a_ops = &ntfs_aops; ++ } ++ attr->nres.run_off = attr->name_off; ++out: ++ attr->flags = new_aflags; ++ mi->dirty = true; ++ ++ return 0; ++} ++ ++/* ++ * ni_parse_reparse ++ * ++ * buffer is at least 24 bytes ++ */ ++enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, ++ void *buffer) ++{ ++ const struct REPARSE_DATA_BUFFER *rp = NULL; ++ u8 bits; ++ u16 len; ++ typeof(rp->CompressReparseBuffer) *cmpr; ++ ++ static_assert(sizeof(struct REPARSE_DATA_BUFFER) <= 24); ++ ++ /* Try to estimate reparse point */ ++ if (!attr->non_res) { ++ rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER)); ++ } else if (le64_to_cpu(attr->nres.data_size) >= ++ sizeof(struct REPARSE_DATA_BUFFER)) { ++ struct runs_tree run; ++ ++ run_init(&run); ++ ++ if (!attr_load_runs_vcn(ni, ATTR_REPARSE, NULL, 0, &run, 0) && ++ !ntfs_read_run_nb(ni->mi.sbi, &run, 0, buffer, ++ sizeof(struct REPARSE_DATA_BUFFER), ++ NULL)) { ++ rp = buffer; ++ } ++ ++ run_close(&run); ++ } ++ ++ if (!rp) ++ return REPARSE_NONE; ++ ++ len = le16_to_cpu(rp->ReparseDataLength); ++ switch (rp->ReparseTag) { ++ case (IO_REPARSE_TAG_MICROSOFT | IO_REPARSE_TAG_SYMBOLIC_LINK): ++ break; /* Symbolic link */ ++ case IO_REPARSE_TAG_MOUNT_POINT: ++ break; /* Mount points and junctions */ ++ case IO_REPARSE_TAG_SYMLINK: ++ break; ++ case IO_REPARSE_TAG_COMPRESS: ++ /* ++ * WOF - Windows Overlay Filter - used to compress files with lzx/xpress ++ * Unlike native NTFS file compression, the Windows Overlay Filter supports ++ * only read operations. This means that it doesn�t need to sector-align each ++ * compressed chunk, so the compressed data can be packed more tightly together. ++ * If you open the file for writing, the Windows Overlay Filter just decompresses ++ * the entire file, turning it back into a plain file. ++ * ++ * ntfs3 driver decompresses the entire file only on write or change size requests ++ */ ++ ++ cmpr = &rp->CompressReparseBuffer; ++ if (len < sizeof(*cmpr) || ++ cmpr->WofVersion != WOF_CURRENT_VERSION || ++ cmpr->WofProvider != WOF_PROVIDER_SYSTEM || ++ cmpr->ProviderVer != WOF_PROVIDER_CURRENT_VERSION) { ++ return REPARSE_NONE; ++ } ++ ++ switch (cmpr->CompressionFormat) { ++ case WOF_COMPRESSION_XPRESS4K: ++ bits = 0xc; // 4k ++ break; ++ case WOF_COMPRESSION_XPRESS8K: ++ bits = 0xd; // 8k ++ break; ++ case WOF_COMPRESSION_XPRESS16K: ++ bits = 0xe; // 16k ++ break; ++ case WOF_COMPRESSION_LZX32K: ++ bits = 0xf; // 32k ++ break; ++ default: ++ bits = 0x10; // 64k ++ break; ++ } ++ ni_set_ext_compress_bits(ni, bits); ++ return REPARSE_COMPRESSED; ++ ++ case IO_REPARSE_TAG_DEDUP: ++ ni->ni_flags |= NI_FLAG_DEDUPLICATED; ++ return REPARSE_DEDUPLICATED; ++ ++ default: ++ if (rp->ReparseTag & IO_REPARSE_TAG_NAME_SURROGATE) ++ break; ++ ++ return REPARSE_NONE; ++ } ++ ++ /* Looks like normal symlink */ ++ return REPARSE_LINK; ++} ++ ++/* ++ * helper for file_fiemap ++ * assumed ni_lock ++ * TODO: less aggressive locks ++ */ ++int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, ++ __u64 vbo, __u64 len) ++{ ++ int err = 0; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ u8 cluster_bits = sbi->cluster_bits; ++ struct runs_tree *run; ++ struct rw_semaphore *run_lock; ++ struct ATTRIB *attr; ++ CLST vcn = vbo >> cluster_bits; ++ CLST lcn, clen; ++ u64 valid = ni->i_valid; ++ u64 lbo, bytes; ++ u64 end, alloc_size; ++ size_t idx = -1; ++ u32 flags; ++ bool ok; ++ ++ if (S_ISDIR(ni->vfs_inode.i_mode)) { ++ run = &ni->dir.alloc_run; ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME, ++ ARRAY_SIZE(I30_NAME), NULL, NULL); ++ run_lock = &ni->dir.run_lock; ++ } else { ++ run = &ni->file.run; ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, ++ NULL); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ if (is_attr_compressed(attr)) { ++ /*unfortunately cp -r incorrectly treats compressed clusters*/ ++ err = -EOPNOTSUPP; ++ ntfs_inode_warn( ++ &ni->vfs_inode, ++ "fiemap is not supported for compressed file (cp -r)"); ++ goto out; ++ } ++ run_lock = &ni->file.run_lock; ++ } ++ ++ if (!attr || !attr->non_res) { ++ err = fiemap_fill_next_extent( ++ fieinfo, 0, 0, ++ attr ? le32_to_cpu(attr->res.data_size) : 0, ++ FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_LAST | ++ FIEMAP_EXTENT_MERGED); ++ goto out; ++ } ++ ++ end = vbo + len; ++ alloc_size = le64_to_cpu(attr->nres.alloc_size); ++ if (end > alloc_size) ++ end = alloc_size; ++ ++ down_read(run_lock); ++ ++ while (vbo < end) { ++ if (idx == -1) { ++ ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); ++ } else { ++ CLST vcn_next = vcn; ++ ++ ok = run_get_entry(run, ++idx, &vcn, &lcn, &clen) && ++ vcn == vcn_next; ++ if (!ok) ++ vcn = vcn_next; ++ } ++ ++ if (!ok) { ++ up_read(run_lock); ++ down_write(run_lock); ++ ++ err = attr_load_runs_vcn(ni, attr->type, ++ attr_name(attr), ++ attr->name_len, run, vcn); ++ ++ up_write(run_lock); ++ down_read(run_lock); ++ ++ if (err) ++ break; ++ ++ ok = run_lookup_entry(run, vcn, &lcn, &clen, &idx); ++ ++ if (!ok) { ++ err = -EINVAL; ++ break; ++ } ++ } ++ ++ if (!clen) { ++ err = -EINVAL; // ? ++ break; ++ } ++ ++ if (lcn == SPARSE_LCN) { ++ vcn += clen; ++ vbo = (u64)vcn << cluster_bits; ++ continue; ++ } ++ ++ flags = FIEMAP_EXTENT_MERGED; ++ if (S_ISDIR(ni->vfs_inode.i_mode)) { ++ ; ++ } else if (is_attr_compressed(attr)) { ++ CLST clst_data; ++ ++ err = attr_is_frame_compressed( ++ ni, attr, vcn >> attr->nres.c_unit, &clst_data); ++ if (err) ++ break; ++ if (clst_data < NTFS_LZNT_CLUSTERS) ++ flags |= FIEMAP_EXTENT_ENCODED; ++ } else if (is_attr_encrypted(attr)) { ++ flags |= FIEMAP_EXTENT_DATA_ENCRYPTED; ++ } ++ ++ vbo = (u64)vcn << cluster_bits; ++ bytes = (u64)clen << cluster_bits; ++ lbo = (u64)lcn << cluster_bits; ++ ++ vcn += clen; ++ ++ if (vbo + bytes >= end) { ++ bytes = end - vbo; ++ flags |= FIEMAP_EXTENT_LAST; ++ } ++ ++ if (vbo + bytes <= valid) { ++ ; ++ } else if (vbo >= valid) { ++ flags |= FIEMAP_EXTENT_UNWRITTEN; ++ } else { ++ /* vbo < valid && valid < vbo + bytes */ ++ u64 dlen = valid - vbo; ++ ++ err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen, ++ flags); ++ if (err < 0) ++ break; ++ if (err == 1) { ++ err = 0; ++ break; ++ } ++ ++ vbo = valid; ++ bytes -= dlen; ++ if (!bytes) ++ continue; ++ ++ lbo += dlen; ++ flags |= FIEMAP_EXTENT_UNWRITTEN; ++ } ++ ++ err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags); ++ if (err < 0) ++ break; ++ if (err == 1) { ++ err = 0; ++ break; ++ } ++ ++ vbo += bytes; ++ } ++ ++ up_read(run_lock); ++ ++out: ++ return err; ++} ++ ++/* ++ * When decompressing, we typically obtain more than one page per reference. ++ * We inject the additional pages into the page cache. ++ */ ++int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) ++{ ++ int err; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct address_space *mapping = page->mapping; ++ pgoff_t index = page->index; ++ u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT; ++ struct page **pages = NULL; /*array of at most 16 pages. stack?*/ ++ u8 frame_bits; ++ CLST frame; ++ u32 i, idx, frame_size, pages_per_frame; ++ gfp_t gfp_mask; ++ struct page *pg; ++ ++ if (vbo >= ni->vfs_inode.i_size) { ++ SetPageUptodate(page); ++ err = 0; ++ goto out; ++ } ++ ++ if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) { ++ /* xpress or lzx */ ++ frame_bits = ni_ext_compress_bits(ni); ++ } else { ++ /* lznt compression*/ ++ frame_bits = NTFS_LZNT_CUNIT + sbi->cluster_bits; ++ } ++ frame_size = 1u << frame_bits; ++ frame = vbo >> frame_bits; ++ frame_vbo = (u64)frame << frame_bits; ++ idx = (vbo - frame_vbo) >> PAGE_SHIFT; ++ ++ pages_per_frame = frame_size >> PAGE_SHIFT; ++ pages = ntfs_zalloc(pages_per_frame * sizeof(struct page *)); ++ if (!pages) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ pages[idx] = page; ++ index = frame_vbo >> PAGE_SHIFT; ++ gfp_mask = mapping_gfp_mask(mapping); ++ ++ for (i = 0; i < pages_per_frame; i++, index++) { ++ if (i == idx) ++ continue; ++ ++ pg = find_or_create_page(mapping, index, gfp_mask); ++ if (!pg) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ pages[i] = pg; ++ } ++ ++ err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame); ++ ++out1: ++ if (err) ++ SetPageError(page); ++ ++ for (i = 0; i < pages_per_frame; i++) { ++ pg = pages[i]; ++ if (i == idx) ++ continue; ++ unlock_page(pg); ++ put_page(pg); ++ } ++ ++out: ++ /* At this point, err contains 0 or -EIO depending on the "critical" page */ ++ ntfs_free(pages); ++ unlock_page(page); ++ ++ return err; ++} ++ ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++/* ++ * decompress lzx/xpress compressed file ++ * remove ATTR_DATA::WofCompressedData ++ * remove ATTR_REPARSE ++ */ ++int ni_decompress_file(struct ntfs_inode *ni) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct inode *inode = &ni->vfs_inode; ++ loff_t i_size = inode->i_size; ++ struct address_space *mapping = inode->i_mapping; ++ gfp_t gfp_mask = mapping_gfp_mask(mapping); ++ struct page **pages = NULL; ++ struct ATTR_LIST_ENTRY *le; ++ struct ATTRIB *attr; ++ CLST vcn, cend, lcn, clen, end; ++ pgoff_t index; ++ u64 vbo; ++ u8 frame_bits; ++ u32 i, frame_size, pages_per_frame, bytes; ++ struct mft_inode *mi; ++ int err; ++ ++ /* clusters for decompressed data*/ ++ cend = bytes_to_cluster(sbi, i_size); ++ ++ if (!i_size) ++ goto remove_wof; ++ ++ /* check in advance */ ++ if (cend > wnd_zeroes(&sbi->used.bitmap)) { ++ err = -ENOSPC; ++ goto out; ++ } ++ ++ frame_bits = ni_ext_compress_bits(ni); ++ frame_size = 1u << frame_bits; ++ pages_per_frame = frame_size >> PAGE_SHIFT; ++ pages = ntfs_zalloc(pages_per_frame * sizeof(struct page *)); ++ if (!pages) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* ++ * Step 1: decompress data and copy to new allocated clusters ++ */ ++ index = 0; ++ for (vbo = 0; vbo < i_size; vbo += bytes) { ++ u32 nr_pages; ++ bool new; ++ ++ if (vbo + frame_size > i_size) { ++ bytes = i_size - vbo; ++ nr_pages = (bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ } else { ++ nr_pages = pages_per_frame; ++ bytes = frame_size; ++ } ++ ++ end = bytes_to_cluster(sbi, vbo + bytes); ++ ++ for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) { ++ err = attr_data_get_block(ni, vcn, cend - vcn, &lcn, ++ &clen, &new); ++ if (err) ++ goto out; ++ } ++ ++ for (i = 0; i < pages_per_frame; i++, index++) { ++ struct page *pg; ++ ++ pg = find_or_create_page(mapping, index, gfp_mask); ++ if (!pg) { ++ while (i--) { ++ unlock_page(pages[i]); ++ put_page(pages[i]); ++ } ++ err = -ENOMEM; ++ goto out; ++ } ++ pages[i] = pg; ++ } ++ ++ err = ni_read_frame(ni, vbo, pages, pages_per_frame); ++ ++ if (!err) { ++ down_read(&ni->file.run_lock); ++ err = ntfs_bio_pages(sbi, &ni->file.run, pages, ++ nr_pages, vbo, bytes, ++ REQ_OP_WRITE); ++ up_read(&ni->file.run_lock); ++ } ++ ++ for (i = 0; i < pages_per_frame; i++) { ++ unlock_page(pages[i]); ++ put_page(pages[i]); ++ } ++ ++ if (err) ++ goto out; ++ ++ cond_resched(); ++ } ++ ++remove_wof: ++ /* ++ * Step 2: deallocate attributes ATTR_DATA::WofCompressedData and ATTR_REPARSE ++ */ ++ attr = NULL; ++ le = NULL; ++ while ((attr = ni_enum_attr_ex(ni, attr, &le, NULL))) { ++ CLST svcn, evcn; ++ u32 asize, roff; ++ ++ if (attr->type == ATTR_REPARSE) { ++ struct MFT_REF ref; ++ ++ mi_get_ref(&ni->mi, &ref); ++ ntfs_remove_reparse(sbi, 0, &ref); ++ } ++ ++ if (!attr->non_res) ++ continue; ++ ++ if (attr->type != ATTR_REPARSE && ++ (attr->type != ATTR_DATA || ++ attr->name_len != ARRAY_SIZE(WOF_NAME) || ++ memcmp(attr_name(attr), WOF_NAME, sizeof(WOF_NAME)))) ++ continue; ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ ++ if (evcn + 1 <= svcn) ++ continue; ++ ++ asize = le32_to_cpu(attr->size); ++ roff = le16_to_cpu(attr->nres.run_off); ++ ++ /*run==1 means unpack and deallocate*/ ++ run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn, ++ Add2Ptr(attr, roff), asize - roff); ++ } ++ ++ /* ++ * Step 3: remove attribute ATTR_DATA::WofCompressedData ++ */ ++ err = ni_remove_attr(ni, ATTR_DATA, WOF_NAME, ARRAY_SIZE(WOF_NAME), ++ false, NULL); ++ if (err) ++ goto out; ++ ++ /* ++ * Step 4: remove ATTR_REPARSE ++ */ ++ err = ni_remove_attr(ni, ATTR_REPARSE, NULL, 0, false, NULL); ++ if (err) ++ goto out; ++ ++ /* ++ * Step 5: remove sparse flag from data attribute ++ */ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (attr->non_res && is_attr_sparsed(attr)) { ++ /* sparsed attribute header is 8 bytes bigger than normal*/ ++ struct MFT_REC *rec = mi->mrec; ++ u32 used = le32_to_cpu(rec->used); ++ u32 asize = le32_to_cpu(attr->size); ++ u16 roff = le16_to_cpu(attr->nres.run_off); ++ char *rbuf = Add2Ptr(attr, roff); ++ ++ memmove(rbuf - 8, rbuf, used - PtrOffset(rec, rbuf)); ++ attr->size = cpu_to_le32(asize - 8); ++ attr->flags &= ~ATTR_FLAG_SPARSED; ++ attr->nres.run_off = cpu_to_le16(roff - 8); ++ attr->nres.c_unit = 0; ++ rec->used = cpu_to_le32(used - 8); ++ mi->dirty = true; ++ ni->std_fa &= ~(FILE_ATTRIBUTE_SPARSE_FILE | ++ FILE_ATTRIBUTE_REPARSE_POINT); ++ ++ mark_inode_dirty(inode); ++ } ++ ++ /* clear cached flag */ ++ ni->ni_flags &= ~NI_FLAG_COMPRESSED_MASK; ++ if (ni->file.offs_page) { ++ put_page(ni->file.offs_page); ++ ni->file.offs_page = NULL; ++ } ++ mapping->a_ops = &ntfs_aops; ++ ++out: ++ ntfs_free(pages); ++ if (err) { ++ make_bad_inode(inode); ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ } ++ ++ return err; ++} ++ ++/* external compression lzx/xpress */ ++static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, ++ size_t cmpr_size, void *unc, size_t unc_size, ++ u32 frame_size) ++{ ++ int err; ++ void *ctx; ++ ++ if (cmpr_size == unc_size) { ++ /* frame not compressed */ ++ memcpy(unc, cmpr, unc_size); ++ return 0; ++ } ++ ++ err = 0; ++ if (frame_size == 0x8000) { ++ mutex_lock(&sbi->compress.mtx_lzx); ++ /* LZX: frame compressed */ ++ ctx = sbi->compress.lzx; ++ if (!ctx) { ++ /* Lazy initialize lzx decompress context */ ++ ctx = lzx_allocate_decompressor(); ++ if (!ctx) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ sbi->compress.lzx = ctx; ++ } ++ ++ if (lzx_decompress(ctx, cmpr, cmpr_size, unc, unc_size)) { ++ /* treat all errors as "invalid argument" */ ++ err = -EINVAL; ++ } ++out1: ++ mutex_unlock(&sbi->compress.mtx_lzx); ++ } else { ++ /* XPRESS: frame compressed */ ++ mutex_lock(&sbi->compress.mtx_xpress); ++ ctx = sbi->compress.xpress; ++ if (!ctx) { ++ /* Lazy initialize xpress decompress context */ ++ ctx = xpress_allocate_decompressor(); ++ if (!ctx) { ++ err = -ENOMEM; ++ goto out2; ++ } ++ ++ sbi->compress.xpress = ctx; ++ } ++ ++ if (xpress_decompress(ctx, cmpr, cmpr_size, unc, unc_size)) { ++ /* treat all errors as "invalid argument" */ ++ err = -EINVAL; ++ } ++out2: ++ mutex_unlock(&sbi->compress.mtx_xpress); ++ } ++ return err; ++} ++#endif ++ ++/* ++ * ni_read_frame ++ * ++ * pages - array of locked pages ++ */ ++int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, ++ u32 pages_per_frame) ++{ ++ int err; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ u8 cluster_bits = sbi->cluster_bits; ++ char *frame_ondisk = NULL; ++ char *frame_mem = NULL; ++ struct page **pages_disk = NULL; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct runs_tree *run = &ni->file.run; ++ u64 valid_size = ni->i_valid; ++ u64 vbo_disk; ++ size_t unc_size; ++ u32 frame_size, i, npages_disk, ondisk_size; ++ struct page *pg; ++ struct ATTRIB *attr; ++ CLST frame, clst_data; ++ ++ /* ++ * To simplify decompress algorithm do vmap for source and target pages ++ */ ++ for (i = 0; i < pages_per_frame; i++) ++ kmap(pages[i]); ++ ++ frame_size = pages_per_frame << PAGE_SHIFT; ++ frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL); ++ if (!frame_mem) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ attr = ni_find_attr(ni, NULL, &le, ATTR_DATA, NULL, 0, NULL, NULL); ++ if (!attr) { ++ err = -ENOENT; ++ goto out1; ++ } ++ ++ if (!attr->non_res) { ++ u32 data_size = le32_to_cpu(attr->res.data_size); ++ ++ memset(frame_mem, 0, frame_size); ++ if (frame_vbo < data_size) { ++ ondisk_size = data_size - frame_vbo; ++ memcpy(frame_mem, resident_data(attr) + frame_vbo, ++ min(ondisk_size, frame_size)); ++ } ++ err = 0; ++ goto out1; ++ } ++ ++ if (frame_vbo >= valid_size) { ++ memset(frame_mem, 0, frame_size); ++ err = 0; ++ goto out1; ++ } ++ ++ if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) { ++#ifndef CONFIG_NTFS3_LZX_XPRESS ++ err = -EOPNOTSUPP; ++ goto out1; ++#else ++ u32 frame_bits = ni_ext_compress_bits(ni); ++ u64 frame64 = frame_vbo >> frame_bits; ++ u64 frames, vbo_data; ++ ++ if (frame_size != (1u << frame_bits)) { ++ err = -EINVAL; ++ goto out1; ++ } ++ switch (frame_size) { ++ case 0x1000: ++ case 0x2000: ++ case 0x4000: ++ case 0x8000: ++ break; ++ default: ++ /* unknown compression */ ++ err = -EOPNOTSUPP; ++ goto out1; ++ } ++ ++ attr = ni_find_attr(ni, attr, &le, ATTR_DATA, WOF_NAME, ++ ARRAY_SIZE(WOF_NAME), NULL, NULL); ++ if (!attr) { ++ ntfs_inode_err( ++ &ni->vfs_inode, ++ "external compressed file should contains data attribute \"WofCompressedData\""); ++ err = -EINVAL; ++ goto out1; ++ } ++ ++ if (!attr->non_res) { ++ run = NULL; ++ } else { ++ run = run_alloc(); ++ if (!run) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ } ++ ++ frames = (ni->vfs_inode.i_size - 1) >> frame_bits; ++ ++ err = attr_wof_frame_info(ni, attr, run, frame64, frames, ++ frame_bits, &ondisk_size, &vbo_data); ++ if (err) ++ goto out2; ++ ++ if (frame64 == frames) { ++ unc_size = 1 + ((ni->vfs_inode.i_size - 1) & ++ (frame_size - 1)); ++ ondisk_size = attr_size(attr) - vbo_data; ++ } else { ++ unc_size = frame_size; ++ } ++ ++ if (ondisk_size > frame_size) { ++ err = -EINVAL; ++ goto out2; ++ } ++ ++ if (!attr->non_res) { ++ if (vbo_data + ondisk_size > ++ le32_to_cpu(attr->res.data_size)) { ++ err = -EINVAL; ++ goto out1; ++ } ++ ++ err = decompress_lzx_xpress( ++ sbi, Add2Ptr(resident_data(attr), vbo_data), ++ ondisk_size, frame_mem, unc_size, frame_size); ++ goto out1; ++ } ++ vbo_disk = vbo_data; ++ /* load all runs to read [vbo_disk-vbo_to) */ ++ err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME, ++ ARRAY_SIZE(WOF_NAME), run, vbo_disk, ++ vbo_data + ondisk_size); ++ if (err) ++ goto out2; ++ npages_disk = (ondisk_size + (vbo_disk & (PAGE_SIZE - 1)) + ++ PAGE_SIZE - 1) >> ++ PAGE_SHIFT; ++#endif ++ } else if (is_attr_compressed(attr)) { ++ /* lznt compression*/ ++ if (sbi->cluster_size > NTFS_LZNT_MAX_CLUSTER) { ++ err = -EOPNOTSUPP; ++ goto out1; ++ } ++ ++ if (attr->nres.c_unit != NTFS_LZNT_CUNIT) { ++ err = -EOPNOTSUPP; ++ goto out1; ++ } ++ ++ down_write(&ni->file.run_lock); ++ run_truncate_around(run, le64_to_cpu(attr->nres.svcn)); ++ frame = frame_vbo >> (cluster_bits + NTFS_LZNT_CUNIT); ++ err = attr_is_frame_compressed(ni, attr, frame, &clst_data); ++ up_write(&ni->file.run_lock); ++ if (err) ++ goto out1; ++ ++ if (!clst_data) { ++ memset(frame_mem, 0, frame_size); ++ goto out1; ++ } ++ ++ frame_size = sbi->cluster_size << NTFS_LZNT_CUNIT; ++ ondisk_size = clst_data << cluster_bits; ++ ++ if (clst_data >= NTFS_LZNT_CLUSTERS) { ++ /* frame is not compressed */ ++ down_read(&ni->file.run_lock); ++ err = ntfs_bio_pages(sbi, run, pages, pages_per_frame, ++ frame_vbo, ondisk_size, ++ REQ_OP_READ); ++ up_read(&ni->file.run_lock); ++ goto out1; ++ } ++ vbo_disk = frame_vbo; ++ npages_disk = (ondisk_size + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ } else { ++ __builtin_unreachable(); ++ err = -EINVAL; ++ goto out1; ++ } ++ ++ pages_disk = ntfs_zalloc(npages_disk * sizeof(struct page *)); ++ if (!pages_disk) { ++ err = -ENOMEM; ++ goto out2; ++ } ++ ++ for (i = 0; i < npages_disk; i++) { ++ pg = alloc_page(GFP_KERNEL); ++ if (!pg) { ++ err = -ENOMEM; ++ goto out3; ++ } ++ pages_disk[i] = pg; ++ lock_page(pg); ++ kmap(pg); ++ } ++ ++ /* read 'ondisk_size' bytes from disk */ ++ down_read(&ni->file.run_lock); ++ err = ntfs_bio_pages(sbi, run, pages_disk, npages_disk, vbo_disk, ++ ondisk_size, REQ_OP_READ); ++ up_read(&ni->file.run_lock); ++ if (err) ++ goto out3; ++ ++ /* ++ * To simplify decompress algorithm do vmap for source and target pages ++ */ ++ frame_ondisk = vmap(pages_disk, npages_disk, VM_MAP, PAGE_KERNEL_RO); ++ if (!frame_ondisk) { ++ err = -ENOMEM; ++ goto out3; ++ } ++ ++ /* decompress: frame_ondisk -> frame_mem */ ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ if (run != &ni->file.run) { ++ /* LZX or XPRESS */ ++ err = decompress_lzx_xpress( ++ sbi, frame_ondisk + (vbo_disk & (PAGE_SIZE - 1)), ++ ondisk_size, frame_mem, unc_size, frame_size); ++ } else ++#endif ++ { ++ /* LZNT - native ntfs compression */ ++ unc_size = decompress_lznt(frame_ondisk, ondisk_size, frame_mem, ++ frame_size); ++ if ((ssize_t)unc_size < 0) ++ err = unc_size; ++ else if (!unc_size || unc_size > frame_size) ++ err = -EINVAL; ++ } ++ if (!err && valid_size < frame_vbo + frame_size) { ++ size_t ok = valid_size - frame_vbo; ++ ++ memset(frame_mem + ok, 0, frame_size - ok); ++ } ++ ++ vunmap(frame_ondisk); ++ ++out3: ++ for (i = 0; i < npages_disk; i++) { ++ pg = pages_disk[i]; ++ if (pg) { ++ kunmap(pg); ++ unlock_page(pg); ++ put_page(pg); ++ } ++ } ++ ntfs_free(pages_disk); ++ ++out2: ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ if (run != &ni->file.run) ++ run_free(run); ++#endif ++out1: ++ vunmap(frame_mem); ++out: ++ for (i = 0; i < pages_per_frame; i++) { ++ pg = pages[i]; ++ kunmap(pg); ++ ClearPageError(pg); ++ SetPageUptodate(pg); ++ } ++ ++ return err; ++} ++ ++/* ++ * ni_write_frame ++ * ++ * pages - array of locked pages ++ */ ++int ni_write_frame(struct ntfs_inode *ni, struct page **pages, ++ u32 pages_per_frame) ++{ ++ int err; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ u8 frame_bits = NTFS_LZNT_CUNIT + sbi->cluster_bits; ++ u32 frame_size = sbi->cluster_size << NTFS_LZNT_CUNIT; ++ u64 frame_vbo = (u64)pages[0]->index << PAGE_SHIFT; ++ CLST frame = frame_vbo >> frame_bits; ++ char *frame_ondisk = NULL; ++ struct page **pages_disk = NULL; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ char *frame_mem; ++ struct ATTRIB *attr; ++ struct mft_inode *mi; ++ u32 i; ++ struct page *pg; ++ size_t compr_size, ondisk_size; ++ struct lznt *lznt; ++ ++ attr = ni_find_attr(ni, NULL, &le, ATTR_DATA, NULL, 0, NULL, &mi); ++ if (!attr) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ if (WARN_ON(!is_attr_compressed(attr))) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (sbi->cluster_size > NTFS_LZNT_MAX_CLUSTER) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } ++ ++ if (!attr->non_res) { ++ down_write(&ni->file.run_lock); ++ err = attr_make_nonresident(ni, attr, le, mi, ++ le32_to_cpu(attr->res.data_size), ++ &ni->file.run, &attr, pages[0]); ++ up_write(&ni->file.run_lock); ++ if (err) ++ goto out; ++ } ++ ++ if (attr->nres.c_unit != NTFS_LZNT_CUNIT) { ++ err = -EOPNOTSUPP; ++ goto out; ++ } ++ ++ pages_disk = ntfs_zalloc(pages_per_frame * sizeof(struct page *)); ++ if (!pages_disk) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ for (i = 0; i < pages_per_frame; i++) { ++ pg = alloc_page(GFP_KERNEL); ++ if (!pg) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ pages_disk[i] = pg; ++ lock_page(pg); ++ kmap(pg); ++ } ++ ++ /* ++ * To simplify compress algorithm do vmap for source and target pages ++ */ ++ frame_ondisk = vmap(pages_disk, pages_per_frame, VM_MAP, PAGE_KERNEL); ++ if (!frame_ondisk) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ for (i = 0; i < pages_per_frame; i++) ++ kmap(pages[i]); ++ ++ /* map in-memory frame for read-only */ ++ frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL_RO); ++ if (!frame_mem) { ++ err = -ENOMEM; ++ goto out2; ++ } ++ ++ mutex_lock(&sbi->compress.mtx_lznt); ++ lznt = NULL; ++ if (!sbi->compress.lznt) { ++ /* ++ * lznt implements two levels of compression: ++ * 0 - standard compression ++ * 1 - best compression, requires a lot of cpu ++ * use mount option? ++ */ ++ lznt = get_lznt_ctx(0); ++ if (!lznt) { ++ mutex_unlock(&sbi->compress.mtx_lznt); ++ err = -ENOMEM; ++ goto out3; ++ } ++ ++ sbi->compress.lznt = lznt; ++ lznt = NULL; ++ } ++ ++ /* compress: frame_mem -> frame_ondisk */ ++ compr_size = compress_lznt(frame_mem, frame_size, frame_ondisk, ++ frame_size, sbi->compress.lznt); ++ mutex_unlock(&sbi->compress.mtx_lznt); ++ ntfs_free(lznt); ++ ++ if (compr_size + sbi->cluster_size > frame_size) { ++ /* frame is not compressed */ ++ compr_size = frame_size; ++ ondisk_size = frame_size; ++ } else if (compr_size) { ++ /* frame is compressed */ ++ ondisk_size = ntfs_up_cluster(sbi, compr_size); ++ memset(frame_ondisk + compr_size, 0, ondisk_size - compr_size); ++ } else { ++ /* frame is sparsed */ ++ ondisk_size = 0; ++ } ++ ++ down_write(&ni->file.run_lock); ++ run_truncate_around(&ni->file.run, le64_to_cpu(attr->nres.svcn)); ++ err = attr_allocate_frame(ni, frame, compr_size, ni->i_valid); ++ up_write(&ni->file.run_lock); ++ if (err) ++ goto out2; ++ ++ if (!ondisk_size) ++ goto out2; ++ ++ down_read(&ni->file.run_lock); ++ err = ntfs_bio_pages(sbi, &ni->file.run, ++ ondisk_size < frame_size ? pages_disk : pages, ++ pages_per_frame, frame_vbo, ondisk_size, ++ REQ_OP_WRITE); ++ up_read(&ni->file.run_lock); ++ ++out3: ++ vunmap(frame_mem); ++ ++out2: ++ for (i = 0; i < pages_per_frame; i++) ++ kunmap(pages[i]); ++ ++ vunmap(frame_ondisk); ++out1: ++ for (i = 0; i < pages_per_frame; i++) { ++ pg = pages_disk[i]; ++ if (pg) { ++ kunmap(pg); ++ unlock_page(pg); ++ put_page(pg); ++ } ++ } ++ ntfs_free(pages_disk); ++out: ++ return err; ++} ++ ++/* ++ * update duplicate info of ATTR_FILE_NAME in MFT and in parent directories ++ */ ++static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, ++ int sync) ++{ ++ struct ATTRIB *attr; ++ struct mft_inode *mi; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct super_block *sb = sbi->sb; ++ bool re_dirty = false; ++ bool active = sb->s_flags & SB_ACTIVE; ++ bool upd_parent = ni->ni_flags & NI_FLAG_UPDATE_PARENT; ++ ++ if (ni->mi.mrec->flags & RECORD_FLAG_DIR) { ++ dup->fa |= FILE_ATTRIBUTE_DIRECTORY; ++ attr = NULL; ++ dup->alloc_size = 0; ++ dup->data_size = 0; ++ } else { ++ dup->fa &= ~FILE_ATTRIBUTE_DIRECTORY; ++ ++ attr = ni_find_attr(ni, NULL, &le, ATTR_DATA, NULL, 0, NULL, ++ &mi); ++ if (!attr) { ++ dup->alloc_size = dup->data_size = 0; ++ } else if (!attr->non_res) { ++ u32 data_size = le32_to_cpu(attr->res.data_size); ++ ++ dup->alloc_size = cpu_to_le64(QuadAlign(data_size)); ++ dup->data_size = cpu_to_le64(data_size); ++ } else { ++ u64 new_valid = ni->i_valid; ++ u64 data_size = le64_to_cpu(attr->nres.data_size); ++ __le64 valid_le; ++ ++ dup->alloc_size = is_attr_ext(attr) ++ ? attr->nres.total_size ++ : attr->nres.alloc_size; ++ dup->data_size = attr->nres.data_size; ++ ++ if (new_valid > data_size) ++ new_valid = data_size; ++ ++ valid_le = cpu_to_le64(new_valid); ++ if (valid_le != attr->nres.valid_size) { ++ attr->nres.valid_size = valid_le; ++ mi->dirty = true; ++ } ++ } ++ } ++ ++ /* TODO: fill reparse info */ ++ dup->reparse = 0; ++ dup->ea_size = 0; ++ ++ if (ni->ni_flags & NI_FLAG_EA) { ++ attr = ni_find_attr(ni, attr, &le, ATTR_EA_INFO, NULL, 0, NULL, ++ NULL); ++ if (attr) { ++ const struct EA_INFO *info; ++ ++ info = resident_data_ex(attr, sizeof(struct EA_INFO)); ++ dup->ea_size = info->size_pack; ++ } ++ } ++ ++ attr = NULL; ++ le = NULL; ++ ++ while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL, ++ &mi))) { ++ struct inode *dir; ++ struct ATTR_FILE_NAME *fname; ++ ++ fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); ++ if (!fname) ++ continue; ++ ++ if (memcmp(&fname->dup, dup, sizeof(fname->dup))) { ++ memcpy(&fname->dup, dup, sizeof(fname->dup)); ++ mi->dirty = true; ++ } else if (!upd_parent) { ++ continue; ++ } ++ ++ if (!active) ++ continue; /*avoid __wait_on_freeing_inode(inode); */ ++ ++ /*ntfs_iget5 may sleep*/ ++ dir = ntfs_iget5(sb, &fname->home, NULL); ++ if (IS_ERR(dir)) { ++ ntfs_inode_warn( ++ &ni->vfs_inode, ++ "failed to open parent directory r=%lx to update", ++ (long)ino_get(&fname->home)); ++ continue; ++ } ++ ++ if (!is_bad_inode(dir)) { ++ struct ntfs_inode *dir_ni = ntfs_i(dir); ++ ++ if (!ni_trylock(dir_ni)) { ++ re_dirty = true; ++ } else { ++ indx_update_dup(dir_ni, sbi, fname, dup, sync); ++ ni_unlock(dir_ni); ++ } ++ } ++ iput(dir); ++ } ++ ++ return re_dirty; ++} ++ ++/* ++ * ni_write_inode ++ * ++ * write mft base record and all subrecords to disk ++ */ ++int ni_write_inode(struct inode *inode, int sync, const char *hint) ++{ ++ int err = 0, err2; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct super_block *sb = inode->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ bool re_dirty = false; ++ struct ATTR_STD_INFO *std; ++ struct rb_node *node, *next; ++ struct NTFS_DUP_INFO dup; ++ ++ if (is_bad_inode(inode) || sb_rdonly(sb)) ++ return 0; ++ ++ if (!ni_trylock(ni)) { ++ /* 'ni' is under modification, skip for now */ ++ mark_inode_dirty_sync(inode); ++ return 0; ++ } ++ ++ if (is_rec_inuse(ni->mi.mrec) && ++ !(sbi->flags & NTFS_FLAGS_LOG_REPLAYING) && inode->i_nlink) { ++ bool modified = false; ++ ++ /* update times in standard attribute */ ++ std = ni_std(ni); ++ if (!std) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Update the access times if they have changed. */ ++ dup.m_time = kernel2nt(&inode->i_mtime); ++ if (std->m_time != dup.m_time) { ++ std->m_time = dup.m_time; ++ modified = true; ++ } ++ ++ dup.c_time = kernel2nt(&inode->i_ctime); ++ if (std->c_time != dup.c_time) { ++ std->c_time = dup.c_time; ++ modified = true; ++ } ++ ++ dup.a_time = kernel2nt(&inode->i_atime); ++ if (std->a_time != dup.a_time) { ++ std->a_time = dup.a_time; ++ modified = true; ++ } ++ ++ dup.fa = ni->std_fa; ++ if (std->fa != dup.fa) { ++ std->fa = dup.fa; ++ modified = true; ++ } ++ ++ if (modified) ++ ni->mi.dirty = true; ++ ++ if (!ntfs_is_meta_file(sbi, inode->i_ino) && ++ (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))) { ++ dup.cr_time = std->cr_time; ++ /* Not critical if this function fail */ ++ re_dirty = ni_update_parent(ni, &dup, sync); ++ ++ if (re_dirty) ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ else ++ ni->ni_flags &= ~NI_FLAG_UPDATE_PARENT; ++ } ++ ++ /* update attribute list */ ++ if (ni->attr_list.size && ni->attr_list.dirty) { ++ if (inode->i_ino != MFT_REC_MFT || sync) { ++ err = ni_try_remove_attr_list(ni); ++ if (err) ++ goto out; ++ } ++ ++ err = al_update(ni); ++ if (err) ++ goto out; ++ } ++ } ++ ++ for (node = rb_first(&ni->mi_tree); node; node = next) { ++ struct mft_inode *mi = rb_entry(node, struct mft_inode, node); ++ bool is_empty; ++ ++ next = rb_next(node); ++ ++ if (!mi->dirty) ++ continue; ++ ++ is_empty = !mi_enum_attr(mi, NULL); ++ ++ if (is_empty) ++ clear_rec_inuse(mi->mrec); ++ ++ err2 = mi_write(mi, sync); ++ if (!err && err2) ++ err = err2; ++ ++ if (is_empty) { ++ ntfs_mark_rec_free(sbi, mi->rno); ++ rb_erase(node, &ni->mi_tree); ++ mi_put(mi); ++ } ++ } ++ ++ if (ni->mi.dirty) { ++ err2 = mi_write(&ni->mi, sync); ++ if (!err && err2) ++ err = err2; ++ } ++out: ++ ni_unlock(ni); ++ ++ if (err) { ++ ntfs_err(sb, "%s r=%lx failed, %d.", hint, inode->i_ino, err); ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ return err; ++ } ++ ++ if (re_dirty && (sb->s_flags & SB_ACTIVE)) ++ mark_inode_dirty_sync(inode); ++ ++ return 0; ++} +diff --git a/src/namei.c b/src/namei.c +new file mode 100644 +index 0000000000000000000000000000000000000000..b1ccd66172f29d2e3a324ab620483c44602294f7 +--- /dev/null ++++ b/src/namei.c +@@ -0,0 +1,539 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* ++ * fill_name_de ++ * ++ * formats NTFS_DE in 'buf' ++ */ ++int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, ++ const struct cpu_str *uni) ++{ ++ int err; ++ struct NTFS_DE *e = buf; ++ u16 data_size; ++ struct ATTR_FILE_NAME *fname = (struct ATTR_FILE_NAME *)(e + 1); ++ ++#ifndef CONFIG_NTFS3_64BIT_CLUSTER ++ e->ref.high = fname->home.high = 0; ++#endif ++ if (uni) { ++#ifdef __BIG_ENDIAN ++ int ulen = uni->len; ++ __le16 *uname = fname->name; ++ const u16 *name_cpu = uni->name; ++ ++ while (ulen--) ++ *uname++ = cpu_to_le16(*name_cpu++); ++#else ++ memcpy(fname->name, uni->name, uni->len * sizeof(u16)); ++#endif ++ fname->name_len = uni->len; ++ ++ } else { ++ /* Convert input string to unicode */ ++ err = ntfs_nls_to_utf16(sbi, name->name, name->len, ++ (struct cpu_str *)&fname->name_len, ++ NTFS_NAME_LEN, UTF16_LITTLE_ENDIAN); ++ if (err < 0) ++ return err; ++ } ++ ++ fname->type = FILE_NAME_POSIX; ++ data_size = fname_full_size(fname); ++ ++ e->size = cpu_to_le16(QuadAlign(data_size) + sizeof(struct NTFS_DE)); ++ e->key_size = cpu_to_le16(data_size); ++ e->flags = 0; ++ e->res = 0; ++ ++ return 0; ++} ++ ++/* ++ * ntfs_lookup ++ * ++ * inode_operations::lookup ++ */ ++static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, ++ u32 flags) ++{ ++ struct ntfs_inode *ni = ntfs_i(dir); ++ struct cpu_str *uni = __getname(); ++ struct inode *inode; ++ int err; ++ ++ if (!uni) ++ inode = ERR_PTR(-ENOMEM); ++ else { ++ err = ntfs_nls_to_utf16(ni->mi.sbi, dentry->d_name.name, ++ dentry->d_name.len, uni, NTFS_NAME_LEN, ++ UTF16_HOST_ENDIAN); ++ if (err < 0) ++ inode = ERR_PTR(err); ++ else { ++ ni_lock(ni); ++ inode = dir_search_u(dir, uni, NULL); ++ ni_unlock(ni); ++ } ++ __putname(uni); ++ } ++ ++ return d_splice_alias(inode, dentry); ++} ++ ++/* ++ * ntfs_create ++ * ++ * inode_operations::create ++ */ ++static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, ++ struct dentry *dentry, umode_t mode, bool excl) ++{ ++ struct ntfs_inode *ni = ntfs_i(dir); ++ struct inode *inode; ++ ++ ni_lock_dir(ni); ++ ++ inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFREG | mode, ++ 0, NULL, 0, NULL); ++ ++ ni_unlock(ni); ++ ++ return IS_ERR(inode) ? PTR_ERR(inode) : 0; ++} ++ ++/* ++ * ntfs_mknod ++ * ++ * inode_operations::mknod ++ */ ++static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, ++ struct dentry *dentry, umode_t mode, dev_t rdev) ++{ ++ struct ntfs_inode *ni = ntfs_i(dir); ++ struct inode *inode; ++ ++ ni_lock_dir(ni); ++ ++ inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, mode, rdev, ++ NULL, 0, NULL); ++ ++ ni_unlock(ni); ++ ++ return IS_ERR(inode) ? PTR_ERR(inode) : 0; ++} ++ ++/* ++ * ntfs_link ++ * ++ * inode_operations::link ++ */ ++static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) ++{ ++ int err; ++ struct inode *inode = d_inode(ode); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ if (S_ISDIR(inode->i_mode)) ++ return -EPERM; ++ ++ if (inode->i_nlink >= NTFS_LINK_MAX) ++ return -EMLINK; ++ ++ ni_lock_dir(ntfs_i(dir)); ++ if (inode != dir) ++ ni_lock(ni); ++ ++ dir->i_ctime = dir->i_mtime = inode->i_ctime = current_time(inode); ++ inc_nlink(inode); ++ ihold(inode); ++ ++ err = ntfs_link_inode(inode, de); ++ if (!err) { ++ mark_inode_dirty(inode); ++ mark_inode_dirty(dir); ++ d_instantiate(de, inode); ++ } else { ++ drop_nlink(inode); ++ iput(inode); ++ } ++ ++ if (inode != dir) ++ ni_unlock(ni); ++ ni_unlock(ntfs_i(dir)); ++ ++ return err; ++} ++ ++/* ++ * ntfs_unlink ++ * ++ * inode_operations::unlink ++ */ ++static int ntfs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ struct ntfs_inode *ni = ntfs_i(dir); ++ int err; ++ ++ ni_lock_dir(ni); ++ ++ err = ntfs_unlink_inode(dir, dentry); ++ ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++/* ++ * ntfs_symlink ++ * ++ * inode_operations::symlink ++ */ ++static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, ++ struct dentry *dentry, const char *symname) ++{ ++ u32 size = strlen(symname); ++ struct inode *inode; ++ struct ntfs_inode *ni = ntfs_i(dir); ++ ++ ni_lock_dir(ni); ++ ++ inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFLNK | 0777, ++ 0, symname, size, NULL); ++ ++ ni_unlock(ni); ++ ++ return IS_ERR(inode) ? PTR_ERR(inode) : 0; ++} ++ ++/* ++ * ntfs_mkdir ++ * ++ * inode_operations::mkdir ++ */ ++static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, ++ struct dentry *dentry, umode_t mode) ++{ ++ struct inode *inode; ++ struct ntfs_inode *ni = ntfs_i(dir); ++ ++ ni_lock_dir(ni); ++ ++ inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFDIR | mode, ++ 0, NULL, 0, NULL); ++ ++ ni_unlock(ni); ++ ++ return IS_ERR(inode) ? PTR_ERR(inode) : 0; ++} ++ ++/* ++ * ntfs_rmdir ++ * ++ * inode_operations::rm_dir ++ */ ++static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ struct ntfs_inode *ni = ntfs_i(dir); ++ int err; ++ ++ ni_lock_dir(ni); ++ ++ err = ntfs_unlink_inode(dir, dentry); ++ ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++/* ++ * ntfs_rename ++ * ++ * inode_operations::rename ++ */ ++static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, ++ struct dentry *old_dentry, struct inode *new_dir, ++ struct dentry *new_dentry, u32 flags) ++{ ++ int err; ++ struct super_block *sb = old_dir->i_sb; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_inode *old_dir_ni = ntfs_i(old_dir); ++ struct ntfs_inode *new_dir_ni = ntfs_i(new_dir); ++ struct ntfs_inode *old_ni; ++ struct ATTR_FILE_NAME *old_name, *new_name, *fname; ++ u8 name_type; ++ bool is_same; ++ struct inode *old_inode, *new_inode; ++ struct NTFS_DE *old_de, *new_de; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ u16 new_de_key_size; ++ ++ static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + SIZEOF_RESIDENT < 1024); ++ static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + sizeof(struct NTFS_DE) < ++ 1024); ++ static_assert(PATH_MAX >= 4 * 1024); ++ ++ if (flags & ~RENAME_NOREPLACE) ++ return -EINVAL; ++ ++ old_inode = d_inode(old_dentry); ++ new_inode = d_inode(new_dentry); ++ ++ old_ni = ntfs_i(old_inode); ++ ++ is_same = old_dentry->d_name.len == new_dentry->d_name.len && ++ !memcmp(old_dentry->d_name.name, new_dentry->d_name.name, ++ old_dentry->d_name.len); ++ ++ if (is_same && old_dir == new_dir) { ++ /* Nothing to do */ ++ err = 0; ++ goto out; ++ } ++ ++ if (ntfs_is_meta_file(sbi, old_inode->i_ino)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (new_inode) { ++ /*target name exists. unlink it*/ ++ dget(new_dentry); ++ ni_lock_dir(new_dir_ni); ++ err = ntfs_unlink_inode(new_dir, new_dentry); ++ ni_unlock(new_dir_ni); ++ dput(new_dentry); ++ if (err) ++ goto out; ++ } ++ ++ /* allocate PATH_MAX bytes */ ++ old_de = __getname(); ++ if (!old_de) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = fill_name_de(sbi, old_de, &old_dentry->d_name, NULL); ++ if (err < 0) ++ goto out1; ++ ++ old_name = (struct ATTR_FILE_NAME *)(old_de + 1); ++ ++ if (is_same) { ++ new_de = old_de; ++ } else { ++ new_de = Add2Ptr(old_de, 1024); ++ err = fill_name_de(sbi, new_de, &new_dentry->d_name, NULL); ++ if (err < 0) ++ goto out1; ++ } ++ ++ ni_lock_dir(old_dir_ni); ++ ni_lock(old_ni); ++ ++ mi_get_ref(&old_dir_ni->mi, &old_name->home); ++ ++ /*get pointer to file_name in mft*/ ++ fname = ni_fname_name(old_ni, (struct cpu_str *)&old_name->name_len, ++ &old_name->home, &le); ++ if (!fname) { ++ err = -EINVAL; ++ goto out2; ++ } ++ ++ /* Copy fname info from record into new fname */ ++ new_name = (struct ATTR_FILE_NAME *)(new_de + 1); ++ memcpy(&new_name->dup, &fname->dup, sizeof(fname->dup)); ++ ++ name_type = paired_name(fname->type); ++ ++ /* remove first name from directory */ ++ err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1, ++ le16_to_cpu(old_de->key_size), sbi); ++ if (err) ++ goto out3; ++ ++ /* remove first name from mft */ ++ err = ni_remove_attr_le(old_ni, attr_from_name(fname), le); ++ if (err) ++ goto out4; ++ ++ le16_add_cpu(&old_ni->mi.mrec->hard_links, -1); ++ old_ni->mi.dirty = true; ++ ++ if (name_type != FILE_NAME_POSIX) { ++ /* get paired name */ ++ fname = ni_fname_type(old_ni, name_type, &le); ++ if (fname) { ++ /* remove second name from directory */ ++ err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, ++ fname, fname_full_size(fname), ++ sbi); ++ if (err) ++ goto out5; ++ ++ /* remove second name from mft */ ++ err = ni_remove_attr_le(old_ni, attr_from_name(fname), ++ le); ++ if (err) ++ goto out6; ++ ++ le16_add_cpu(&old_ni->mi.mrec->hard_links, -1); ++ old_ni->mi.dirty = true; ++ } ++ } ++ ++ /* Add new name */ ++ mi_get_ref(&old_ni->mi, &new_de->ref); ++ mi_get_ref(&ntfs_i(new_dir)->mi, &new_name->home); ++ ++ new_de_key_size = le16_to_cpu(new_de->key_size); ++ ++ /* insert new name in mft */ ++ err = ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0, ++ &attr, NULL); ++ if (err) ++ goto out7; ++ ++ attr->res.flags = RESIDENT_FLAG_INDEXED; ++ ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), new_name, new_de_key_size); ++ ++ le16_add_cpu(&old_ni->mi.mrec->hard_links, 1); ++ old_ni->mi.dirty = true; ++ ++ /* insert new name in directory */ ++ err = indx_insert_entry(&new_dir_ni->dir, new_dir_ni, new_de, sbi, ++ NULL); ++ if (err) ++ goto out8; ++ ++ if (IS_DIRSYNC(new_dir)) ++ err = ntfs_sync_inode(old_inode); ++ else ++ mark_inode_dirty(old_inode); ++ ++ old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); ++ if (IS_DIRSYNC(old_dir)) ++ (void)ntfs_sync_inode(old_dir); ++ else ++ mark_inode_dirty(old_dir); ++ ++ if (old_dir != new_dir) { ++ new_dir->i_mtime = new_dir->i_ctime = old_dir->i_ctime; ++ mark_inode_dirty(new_dir); ++ } ++ ++ if (old_inode) { ++ old_inode->i_ctime = old_dir->i_ctime; ++ mark_inode_dirty(old_inode); ++ } ++ ++ err = 0; ++ /* normal way */ ++ goto out2; ++ ++out8: ++ /* undo ++ * ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0, ++ * &attr, NULL); ++ */ ++ mi_remove_attr(&old_ni->mi, attr); ++out7: ++ /* undo ++ * ni_remove_attr_le(old_ni, attr_from_name(fname), le); ++ */ ++out6: ++ /* undo ++ * indx_delete_entry(&old_dir_ni->dir, old_dir_ni, ++ * fname, fname_full_size(fname), ++ * sbi); ++ */ ++out5: ++ /* undo ++ * ni_remove_attr_le(old_ni, attr_from_name(fname), le); ++ */ ++out4: ++ /* undo: ++ * indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1, ++ * old_de->key_size, NULL); ++ */ ++out3: ++out2: ++ ni_unlock(old_ni); ++ ni_unlock(old_dir_ni); ++out1: ++ __putname(old_de); ++out: ++ return err; ++} ++ ++struct dentry *ntfs3_get_parent(struct dentry *child) ++{ ++ struct inode *inode = d_inode(child); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct ATTRIB *attr = NULL; ++ struct ATTR_FILE_NAME *fname; ++ ++ while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL, ++ NULL))) { ++ fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); ++ if (!fname) ++ continue; ++ ++ return d_obtain_alias( ++ ntfs_iget5(inode->i_sb, &fname->home, NULL)); ++ } ++ ++ return ERR_PTR(-ENOENT); ++} ++ ++// clang-format off ++const struct inode_operations ntfs_dir_inode_operations = { ++ .lookup = ntfs_lookup, ++ .create = ntfs_create, ++ .link = ntfs_link, ++ .unlink = ntfs_unlink, ++ .symlink = ntfs_symlink, ++ .mkdir = ntfs_mkdir, ++ .rmdir = ntfs_rmdir, ++ .mknod = ntfs_mknod, ++ .rename = ntfs_rename, ++ .permission = ntfs_permission, ++ .get_acl = ntfs_get_acl, ++ .set_acl = ntfs_set_acl, ++ .setattr = ntfs3_setattr, ++ .getattr = ntfs_getattr, ++ .listxattr = ntfs_listxattr, ++ .fiemap = ntfs_fiemap, ++}; ++ ++const struct inode_operations ntfs_special_inode_operations = { ++ .setattr = ntfs3_setattr, ++ .getattr = ntfs_getattr, ++ .listxattr = ntfs_listxattr, ++ .get_acl = ntfs_get_acl, ++ .set_acl = ntfs_set_acl, ++}; ++// clang-format on +diff --git a/src/record.c b/src/record.c +new file mode 100644 +index 0000000000000000000000000000000000000000..0d4a6251bddcbd689db9eab4993ccc9675ab3838 +--- /dev/null ++++ b/src/record.c +@@ -0,0 +1,609 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++static inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, ++ const u16 *upcase) ++{ ++ /* First, compare the type codes: */ ++ int diff = le32_to_cpu(left->type) - le32_to_cpu(type); ++ ++ if (diff) ++ return diff; ++ ++ /* ++ * They have the same type code, so we have to compare the names. ++ */ ++ return ntfs_cmp_names(attr_name(left), left->name_len, name, name_len, ++ upcase, true); ++} ++ ++/* ++ * mi_new_attt_id ++ * ++ * returns unused attribute id that is less than mrec->next_attr_id ++ */ ++static __le16 mi_new_attt_id(struct mft_inode *mi) ++{ ++ u16 free_id, max_id, t16; ++ struct MFT_REC *rec = mi->mrec; ++ struct ATTRIB *attr; ++ __le16 id; ++ ++ id = rec->next_attr_id; ++ free_id = le16_to_cpu(id); ++ if (free_id < 0x7FFF) { ++ rec->next_attr_id = cpu_to_le16(free_id + 1); ++ return id; ++ } ++ ++ /* One record can store up to 1024/24 ~= 42 attributes */ ++ free_id = 0; ++ max_id = 0; ++ ++ attr = NULL; ++ ++ for (;;) { ++ attr = mi_enum_attr(mi, attr); ++ if (!attr) { ++ rec->next_attr_id = cpu_to_le16(max_id + 1); ++ mi->dirty = true; ++ return cpu_to_le16(free_id); ++ } ++ ++ t16 = le16_to_cpu(attr->id); ++ if (t16 == free_id) { ++ free_id += 1; ++ attr = NULL; ++ } else if (max_id < t16) ++ max_id = t16; ++ } ++} ++ ++int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi) ++{ ++ int err; ++ struct mft_inode *m = ntfs_zalloc(sizeof(struct mft_inode)); ++ ++ if (!m) ++ return -ENOMEM; ++ ++ err = mi_init(m, sbi, rno); ++ if (err) { ++ ntfs_free(m); ++ return err; ++ } ++ ++ err = mi_read(m, false); ++ if (err) { ++ mi_put(m); ++ return err; ++ } ++ ++ *mi = m; ++ return 0; ++} ++ ++void mi_put(struct mft_inode *mi) ++{ ++ mi_clear(mi); ++ ntfs_free(mi); ++} ++ ++int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno) ++{ ++ mi->sbi = sbi; ++ mi->rno = rno; ++ mi->mrec = ntfs_malloc(sbi->record_size); ++ if (!mi->mrec) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++/* ++ * mi_read ++ * ++ * reads MFT data ++ */ ++int mi_read(struct mft_inode *mi, bool is_mft) ++{ ++ int err; ++ struct MFT_REC *rec = mi->mrec; ++ struct ntfs_sb_info *sbi = mi->sbi; ++ u32 bpr = sbi->record_size; ++ u64 vbo = (u64)mi->rno << sbi->record_bits; ++ struct ntfs_inode *mft_ni = sbi->mft.ni; ++ struct runs_tree *run = mft_ni ? &mft_ni->file.run : NULL; ++ struct rw_semaphore *rw_lock = NULL; ++ ++ if (is_mounted(sbi)) { ++ if (!is_mft) { ++ rw_lock = &mft_ni->file.run_lock; ++ down_read(rw_lock); ++ } ++ } ++ ++ err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb); ++ if (rw_lock) ++ up_read(rw_lock); ++ if (!err) ++ goto ok; ++ ++ if (err == -E_NTFS_FIXUP) { ++ mi->dirty = true; ++ goto ok; ++ } ++ ++ if (err != -ENOENT) ++ goto out; ++ ++ if (rw_lock) { ++ ni_lock(mft_ni); ++ down_write(rw_lock); ++ } ++ err = attr_load_runs_vcn(mft_ni, ATTR_DATA, NULL, 0, &mft_ni->file.run, ++ vbo >> sbi->cluster_bits); ++ if (rw_lock) { ++ up_write(rw_lock); ++ ni_unlock(mft_ni); ++ } ++ if (err) ++ goto out; ++ ++ if (rw_lock) ++ down_read(rw_lock); ++ err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb); ++ if (rw_lock) ++ up_read(rw_lock); ++ ++ if (err == -E_NTFS_FIXUP) { ++ mi->dirty = true; ++ goto ok; ++ } ++ if (err) ++ goto out; ++ ++ok: ++ /* check field 'total' only here */ ++ if (le32_to_cpu(rec->total) != bpr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ return 0; ++ ++out: ++ return err; ++} ++ ++struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) ++{ ++ const struct MFT_REC *rec = mi->mrec; ++ u32 used = le32_to_cpu(rec->used); ++ u32 t32, off, asize; ++ u16 t16; ++ ++ if (!attr) { ++ u32 total = le32_to_cpu(rec->total); ++ ++ off = le16_to_cpu(rec->attr_off); ++ ++ if (used > total) ++ return NULL; ++ ++ if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 || ++ !IsDwordAligned(off)) { ++ return NULL; ++ } ++ ++ /* Skip non-resident records */ ++ if (!is_rec_inuse(rec)) ++ return NULL; ++ ++ attr = Add2Ptr(rec, off); ++ } else { ++ /* Check if input attr inside record */ ++ off = PtrOffset(rec, attr); ++ if (off >= used) ++ return NULL; ++ ++ asize = le32_to_cpu(attr->size); ++ if (asize < SIZEOF_RESIDENT) { ++ /* Impossible 'cause we should not return such attribute */ ++ return NULL; ++ } ++ ++ attr = Add2Ptr(attr, asize); ++ off += asize; ++ } ++ ++ asize = le32_to_cpu(attr->size); ++ ++ /* Can we use the first field (attr->type) */ ++ if (off + 8 > used) { ++ static_assert(QuadAlign(sizeof(enum ATTR_TYPE)) == 8); ++ return NULL; ++ } ++ ++ if (attr->type == ATTR_END) { ++ /* end of enumeration */ ++ return NULL; ++ } ++ ++ /* 0x100 is last known attribute for now*/ ++ t32 = le32_to_cpu(attr->type); ++ if ((t32 & 0xf) || (t32 > 0x100)) ++ return NULL; ++ ++ /* Check boundary */ ++ if (off + asize > used) ++ return NULL; ++ ++ /* Check size of attribute */ ++ if (!attr->non_res) { ++ if (asize < SIZEOF_RESIDENT) ++ return NULL; ++ ++ t16 = le16_to_cpu(attr->res.data_off); ++ ++ if (t16 > asize) ++ return NULL; ++ ++ t32 = le32_to_cpu(attr->res.data_size); ++ if (t16 + t32 > asize) ++ return NULL; ++ ++ return attr; ++ } ++ ++ /* Check some nonresident fields */ ++ if (attr->name_len && ++ le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len > ++ le16_to_cpu(attr->nres.run_off)) { ++ return NULL; ++ } ++ ++ if (attr->nres.svcn || !is_attr_ext(attr)) { ++ if (asize + 8 < SIZEOF_NONRESIDENT) ++ return NULL; ++ ++ if (attr->nres.c_unit) ++ return NULL; ++ } else if (asize + 8 < SIZEOF_NONRESIDENT_EX) ++ return NULL; ++ ++ return attr; ++} ++ ++/* ++ * mi_find_attr ++ * ++ * finds the attribute by type and name and id ++ */ ++struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr, ++ enum ATTR_TYPE type, const __le16 *name, ++ size_t name_len, const __le16 *id) ++{ ++ u32 type_in = le32_to_cpu(type); ++ u32 atype; ++ ++next_attr: ++ attr = mi_enum_attr(mi, attr); ++ if (!attr) ++ return NULL; ++ ++ atype = le32_to_cpu(attr->type); ++ if (atype > type_in) ++ return NULL; ++ ++ if (atype < type_in) ++ goto next_attr; ++ ++ if (attr->name_len != name_len) ++ goto next_attr; ++ ++ if (name_len && memcmp(attr_name(attr), name, name_len * sizeof(short))) ++ goto next_attr; ++ ++ if (id && *id != attr->id) ++ goto next_attr; ++ ++ return attr; ++} ++ ++int mi_write(struct mft_inode *mi, int wait) ++{ ++ struct MFT_REC *rec; ++ int err; ++ struct ntfs_sb_info *sbi; ++ ++ if (!mi->dirty) ++ return 0; ++ ++ sbi = mi->sbi; ++ rec = mi->mrec; ++ ++ err = ntfs_write_bh(sbi, &rec->rhdr, &mi->nb, wait); ++ if (err) ++ return err; ++ ++ if (mi->rno < sbi->mft.recs_mirr) ++ sbi->flags |= NTFS_FLAGS_MFTMIRR; ++ ++ mi->dirty = false; ++ ++ return 0; ++} ++ ++int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno, ++ __le16 flags, bool is_mft) ++{ ++ int err; ++ u16 seq = 1; ++ struct MFT_REC *rec; ++ u64 vbo = (u64)rno << sbi->record_bits; ++ ++ err = mi_init(mi, sbi, rno); ++ if (err) ++ return err; ++ ++ rec = mi->mrec; ++ ++ if (rno == MFT_REC_MFT) { ++ ; ++ } else if (rno < MFT_REC_FREE) { ++ seq = rno; ++ } else if (rno >= sbi->mft.used) { ++ ; ++ } else if (mi_read(mi, is_mft)) { ++ ; ++ } else if (rec->rhdr.sign == NTFS_FILE_SIGNATURE) { ++ /* Record is reused. Update its sequence number */ ++ seq = le16_to_cpu(rec->seq) + 1; ++ if (!seq) ++ seq = 1; ++ } ++ ++ memcpy(rec, sbi->new_rec, sbi->record_size); ++ ++ rec->seq = cpu_to_le16(seq); ++ rec->flags = RECORD_FLAG_IN_USE | flags; ++ ++ mi->dirty = true; ++ ++ if (!mi->nb.nbufs) { ++ struct ntfs_inode *ni = sbi->mft.ni; ++ bool lock = false; ++ ++ if (is_mounted(sbi) && !is_mft) { ++ down_read(&ni->file.run_lock); ++ lock = true; ++ } ++ ++ err = ntfs_get_bh(sbi, &ni->file.run, vbo, sbi->record_size, ++ &mi->nb); ++ if (lock) ++ up_read(&ni->file.run_lock); ++ } ++ ++ return err; ++} ++ ++/* ++ * mi_mark_free ++ * ++ * marks record as unused and marks it as free in bitmap ++ */ ++void mi_mark_free(struct mft_inode *mi) ++{ ++ CLST rno = mi->rno; ++ struct ntfs_sb_info *sbi = mi->sbi; ++ ++ if (rno >= MFT_REC_RESERVED && rno < MFT_REC_FREE) { ++ ntfs_clear_mft_tail(sbi, rno, rno + 1); ++ mi->dirty = false; ++ return; ++ } ++ ++ if (mi->mrec) { ++ clear_rec_inuse(mi->mrec); ++ mi->dirty = true; ++ mi_write(mi, 0); ++ } ++ ntfs_mark_rec_free(sbi, rno); ++} ++ ++/* ++ * mi_insert_attr ++ * ++ * reserves space for new attribute ++ * returns not full constructed attribute or NULL if not possible to create ++ */ ++struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, u32 asize, ++ u16 name_off) ++{ ++ size_t tail; ++ struct ATTRIB *attr; ++ __le16 id; ++ struct MFT_REC *rec = mi->mrec; ++ struct ntfs_sb_info *sbi = mi->sbi; ++ u32 used = le32_to_cpu(rec->used); ++ const u16 *upcase = sbi->upcase; ++ int diff; ++ ++ /* Can we insert mi attribute? */ ++ if (used + asize > mi->sbi->record_size) ++ return NULL; ++ ++ /* ++ * Scan through the list of attributes to find the point ++ * at which we should insert it. ++ */ ++ attr = NULL; ++ while ((attr = mi_enum_attr(mi, attr))) { ++ diff = compare_attr(attr, type, name, name_len, upcase); ++ if (diff > 0) ++ break; ++ if (diff < 0) ++ continue; ++ ++ if (!is_attr_indexed(attr)) ++ return NULL; ++ break; ++ } ++ ++ if (!attr) { ++ tail = 8; /* not used, just to suppress warning */ ++ attr = Add2Ptr(rec, used - 8); ++ } else { ++ tail = used - PtrOffset(rec, attr); ++ } ++ ++ id = mi_new_attt_id(mi); ++ ++ memmove(Add2Ptr(attr, asize), attr, tail); ++ memset(attr, 0, asize); ++ ++ attr->type = type; ++ attr->size = cpu_to_le32(asize); ++ attr->name_len = name_len; ++ attr->name_off = cpu_to_le16(name_off); ++ attr->id = id; ++ ++ memmove(Add2Ptr(attr, name_off), name, name_len * sizeof(short)); ++ rec->used = cpu_to_le32(used + asize); ++ ++ mi->dirty = true; ++ ++ return attr; ++} ++ ++/* ++ * mi_remove_attr ++ * ++ * removes the attribute from record ++ * NOTE: The source attr will point to next attribute ++ */ ++bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr) ++{ ++ struct MFT_REC *rec = mi->mrec; ++ u32 aoff = PtrOffset(rec, attr); ++ u32 used = le32_to_cpu(rec->used); ++ u32 asize = le32_to_cpu(attr->size); ++ ++ if (aoff + asize > used) ++ return false; ++ ++ used -= asize; ++ memmove(attr, Add2Ptr(attr, asize), used - aoff); ++ rec->used = cpu_to_le32(used); ++ mi->dirty = true; ++ ++ return true; ++} ++ ++/* bytes = "new attribute size" - "old attribute size" */ ++bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes) ++{ ++ struct MFT_REC *rec = mi->mrec; ++ u32 aoff = PtrOffset(rec, attr); ++ u32 total, used = le32_to_cpu(rec->used); ++ u32 nsize, asize = le32_to_cpu(attr->size); ++ u32 rsize = le32_to_cpu(attr->res.data_size); ++ int tail = (int)(used - aoff - asize); ++ int dsize; ++ char *next; ++ ++ if (tail < 0 || aoff >= used) ++ return false; ++ ++ if (!bytes) ++ return true; ++ ++ total = le32_to_cpu(rec->total); ++ next = Add2Ptr(attr, asize); ++ ++ if (bytes > 0) { ++ dsize = QuadAlign(bytes); ++ if (used + dsize > total) ++ return false; ++ nsize = asize + dsize; ++ // move tail ++ memmove(next + dsize, next, tail); ++ memset(next, 0, dsize); ++ used += dsize; ++ rsize += dsize; ++ } else { ++ dsize = QuadAlign(-bytes); ++ if (dsize > asize) ++ return false; ++ nsize = asize - dsize; ++ memmove(next - dsize, next, tail); ++ used -= dsize; ++ rsize -= dsize; ++ } ++ ++ rec->used = cpu_to_le32(used); ++ attr->size = cpu_to_le32(nsize); ++ if (!attr->non_res) ++ attr->res.data_size = cpu_to_le32(rsize); ++ mi->dirty = true; ++ ++ return true; ++} ++ ++int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr, ++ struct runs_tree *run, CLST len) ++{ ++ int err = 0; ++ struct ntfs_sb_info *sbi = mi->sbi; ++ u32 new_run_size; ++ CLST plen; ++ struct MFT_REC *rec = mi->mrec; ++ CLST svcn = le64_to_cpu(attr->nres.svcn); ++ u32 used = le32_to_cpu(rec->used); ++ u32 aoff = PtrOffset(rec, attr); ++ u32 asize = le32_to_cpu(attr->size); ++ char *next = Add2Ptr(attr, asize); ++ u16 run_off = le16_to_cpu(attr->nres.run_off); ++ u32 run_size = asize - run_off; ++ u32 tail = used - aoff - asize; ++ u32 dsize = sbi->record_size - used; ++ ++ /* Make a maximum gap in current record */ ++ memmove(next + dsize, next, tail); ++ ++ /* Pack as much as possible */ ++ err = run_pack(run, svcn, len, Add2Ptr(attr, run_off), run_size + dsize, ++ &plen); ++ if (err < 0) { ++ memmove(next, next + dsize, tail); ++ return err; ++ } ++ ++ new_run_size = QuadAlign(err); ++ ++ memmove(next + new_run_size - run_size, next + dsize, tail); ++ ++ attr->size = cpu_to_le32(asize + new_run_size - run_size); ++ attr->nres.evcn = cpu_to_le64(svcn + plen - 1); ++ rec->used = cpu_to_le32(used + new_run_size - run_size); ++ mi->dirty = true; ++ ++ return 0; ++} +diff --git a/src/run.c b/src/run.c +new file mode 100644 +index 0000000000000000000000000000000000000000..5cdf6efe67e0dbee860ce8462eae8de363ea5671 +--- /dev/null ++++ b/src/run.c +@@ -0,0 +1,1111 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * TODO: try to use extents tree (instead of array) ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* runs_tree is a continues memory. Try to avoid big size */ ++#define NTFS3_RUN_MAX_BYTES 0x10000 ++ ++struct ntfs_run { ++ CLST vcn; /* virtual cluster number */ ++ CLST len; /* length in clusters */ ++ CLST lcn; /* logical cluster number */ ++}; ++ ++/* ++ * run_lookup ++ * ++ * Lookup the index of a MCB entry that is first <= vcn. ++ * case of success it will return non-zero value and set ++ * 'index' parameter to index of entry been found. ++ * case of entry missing from list 'index' will be set to ++ * point to insertion position for the entry question. ++ */ ++bool run_lookup(const struct runs_tree *run, CLST vcn, size_t *index) ++{ ++ size_t min_idx, max_idx, mid_idx; ++ struct ntfs_run *r; ++ ++ if (!run->count) { ++ *index = 0; ++ return false; ++ } ++ ++ min_idx = 0; ++ max_idx = run->count - 1; ++ ++ /* Check boundary cases specially, 'cause they cover the often requests */ ++ r = run->runs; ++ if (vcn < r->vcn) { ++ *index = 0; ++ return false; ++ } ++ ++ if (vcn < r->vcn + r->len) { ++ *index = 0; ++ return true; ++ } ++ ++ r += max_idx; ++ if (vcn >= r->vcn + r->len) { ++ *index = run->count; ++ return false; ++ } ++ ++ if (vcn >= r->vcn) { ++ *index = max_idx; ++ return true; ++ } ++ ++ do { ++ mid_idx = min_idx + ((max_idx - min_idx) >> 1); ++ r = run->runs + mid_idx; ++ ++ if (vcn < r->vcn) { ++ max_idx = mid_idx - 1; ++ if (!mid_idx) ++ break; ++ } else if (vcn >= r->vcn + r->len) { ++ min_idx = mid_idx + 1; ++ } else { ++ *index = mid_idx; ++ return true; ++ } ++ } while (min_idx <= max_idx); ++ ++ *index = max_idx + 1; ++ return false; ++} ++ ++/* ++ * run_consolidate ++ * ++ * consolidate runs starting from a given one. ++ */ ++static void run_consolidate(struct runs_tree *run, size_t index) ++{ ++ size_t i; ++ struct ntfs_run *r = run->runs + index; ++ ++ while (index + 1 < run->count) { ++ /* ++ * I should merge current run with next ++ * if start of the next run lies inside one being tested. ++ */ ++ struct ntfs_run *n = r + 1; ++ CLST end = r->vcn + r->len; ++ CLST dl; ++ ++ /* Stop if runs are not aligned one to another. */ ++ if (n->vcn > end) ++ break; ++ ++ dl = end - n->vcn; ++ ++ /* ++ * If range at index overlaps with next one ++ * then I will either adjust it's start position ++ * or (if completely matches) dust remove one from the list. ++ */ ++ if (dl > 0) { ++ if (n->len <= dl) ++ goto remove_next_range; ++ ++ n->len -= dl; ++ n->vcn += dl; ++ if (n->lcn != SPARSE_LCN) ++ n->lcn += dl; ++ dl = 0; ++ } ++ ++ /* ++ * Stop if sparse mode does not match ++ * both current and next runs. ++ */ ++ if ((n->lcn == SPARSE_LCN) != (r->lcn == SPARSE_LCN)) { ++ index += 1; ++ r = n; ++ continue; ++ } ++ ++ /* ++ * Check if volume block ++ * of a next run lcn does not match ++ * last volume block of the current run. ++ */ ++ if (n->lcn != SPARSE_LCN && n->lcn != r->lcn + r->len) ++ break; ++ ++ /* ++ * Next and current are siblings. ++ * Eat/join. ++ */ ++ r->len += n->len - dl; ++ ++remove_next_range: ++ i = run->count - (index + 1); ++ if (i > 1) ++ memmove(n, n + 1, sizeof(*n) * (i - 1)); ++ ++ run->count -= 1; ++ } ++} ++ ++/* returns true if range [svcn - evcn] is mapped*/ ++bool run_is_mapped_full(const struct runs_tree *run, CLST svcn, CLST evcn) ++{ ++ size_t i; ++ const struct ntfs_run *r, *end; ++ CLST next_vcn; ++ ++ if (!run_lookup(run, svcn, &i)) ++ return false; ++ ++ end = run->runs + run->count; ++ r = run->runs + i; ++ ++ for (;;) { ++ next_vcn = r->vcn + r->len; ++ if (next_vcn > evcn) ++ return true; ++ ++ if (++r >= end) ++ return false; ++ ++ if (r->vcn != next_vcn) ++ return false; ++ } ++} ++ ++bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn, ++ CLST *len, size_t *index) ++{ ++ size_t idx; ++ CLST gap; ++ struct ntfs_run *r; ++ ++ /* Fail immediately if nrun was not touched yet. */ ++ if (!run->runs) ++ return false; ++ ++ if (!run_lookup(run, vcn, &idx)) ++ return false; ++ ++ r = run->runs + idx; ++ ++ if (vcn >= r->vcn + r->len) ++ return false; ++ ++ gap = vcn - r->vcn; ++ if (r->len <= gap) ++ return false; ++ ++ *lcn = r->lcn == SPARSE_LCN ? SPARSE_LCN : (r->lcn + gap); ++ ++ if (len) ++ *len = r->len - gap; ++ if (index) ++ *index = idx; ++ ++ return true; ++} ++ ++/* ++ * run_truncate_head ++ * ++ * decommit the range before vcn ++ */ ++void run_truncate_head(struct runs_tree *run, CLST vcn) ++{ ++ size_t index; ++ struct ntfs_run *r; ++ ++ if (run_lookup(run, vcn, &index)) { ++ r = run->runs + index; ++ ++ if (vcn > r->vcn) { ++ CLST dlen = vcn - r->vcn; ++ ++ r->vcn = vcn; ++ r->len -= dlen; ++ if (r->lcn != SPARSE_LCN) ++ r->lcn += dlen; ++ } ++ ++ if (!index) ++ return; ++ } ++ r = run->runs; ++ memmove(r, r + index, sizeof(*r) * (run->count - index)); ++ ++ run->count -= index; ++ ++ if (!run->count) { ++ ntfs_vfree(run->runs); ++ run->runs = NULL; ++ run->allocated = 0; ++ } ++} ++ ++/* ++ * run_truncate ++ * ++ * decommit the range after vcn ++ */ ++void run_truncate(struct runs_tree *run, CLST vcn) ++{ ++ size_t index; ++ ++ /* ++ * If I hit the range then ++ * I have to truncate one. ++ * If range to be truncated is becoming empty ++ * then it will entirely be removed. ++ */ ++ if (run_lookup(run, vcn, &index)) { ++ struct ntfs_run *r = run->runs + index; ++ ++ r->len = vcn - r->vcn; ++ ++ if (r->len > 0) ++ index += 1; ++ } ++ ++ /* ++ * At this point 'index' is set to ++ * position that should be thrown away (including index itself) ++ * Simple one - just set the limit. ++ */ ++ run->count = index; ++ ++ /* Do not reallocate array 'runs'. Only free if possible */ ++ if (!index) { ++ ntfs_vfree(run->runs); ++ run->runs = NULL; ++ run->allocated = 0; ++ } ++} ++ ++/* trim head and tail if necessary*/ ++void run_truncate_around(struct runs_tree *run, CLST vcn) ++{ ++ run_truncate_head(run, vcn); ++ ++ if (run->count >= NTFS3_RUN_MAX_BYTES / sizeof(struct ntfs_run) / 2) ++ run_truncate(run, (run->runs + (run->count >> 1))->vcn); ++} ++ ++/* ++ * run_add_entry ++ * ++ * sets location to known state. ++ * run to be added may overlap with existing location. ++ * returns false if of memory ++ */ ++bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, ++ bool is_mft) ++{ ++ size_t used, index; ++ struct ntfs_run *r; ++ bool inrange; ++ CLST tail_vcn = 0, tail_len = 0, tail_lcn = 0; ++ bool should_add_tail = false; ++ ++ /* ++ * Lookup the insertion point. ++ * ++ * Execute bsearch for the entry containing ++ * start position question. ++ */ ++ inrange = run_lookup(run, vcn, &index); ++ ++ /* ++ * Shortcut here would be case of ++ * range not been found but one been added ++ * continues previous run. ++ * this case I can directly make use of ++ * existing range as my start point. ++ */ ++ if (!inrange && index > 0) { ++ struct ntfs_run *t = run->runs + index - 1; ++ ++ if (t->vcn + t->len == vcn && ++ (t->lcn == SPARSE_LCN) == (lcn == SPARSE_LCN) && ++ (lcn == SPARSE_LCN || lcn == t->lcn + t->len)) { ++ inrange = true; ++ index -= 1; ++ } ++ } ++ ++ /* ++ * At this point 'index' either points to the range ++ * containing start position or to the insertion position ++ * for a new range. ++ * So first let's check if range I'm probing is here already. ++ */ ++ if (!inrange) { ++requires_new_range: ++ /* ++ * Range was not found. ++ * Insert at position 'index' ++ */ ++ used = run->count * sizeof(struct ntfs_run); ++ ++ /* ++ * Check allocated space. ++ * If one is not enough to get one more entry ++ * then it will be reallocated ++ */ ++ if (run->allocated < used + sizeof(struct ntfs_run)) { ++ size_t bytes; ++ struct ntfs_run *new_ptr; ++ ++ /* Use power of 2 for 'bytes'*/ ++ if (!used) { ++ bytes = 64; ++ } else if (used <= 16 * PAGE_SIZE) { ++ if (is_power_of2(run->allocated)) ++ bytes = run->allocated << 1; ++ else ++ bytes = (size_t)1 ++ << (2 + blksize_bits(used)); ++ } else { ++ bytes = run->allocated + (16 * PAGE_SIZE); ++ } ++ ++ WARN_ON(!is_mft && bytes > NTFS3_RUN_MAX_BYTES); ++ ++ new_ptr = ntfs_vmalloc(bytes); ++ ++ if (!new_ptr) ++ return false; ++ ++ r = new_ptr + index; ++ memcpy(new_ptr, run->runs, ++ index * sizeof(struct ntfs_run)); ++ memcpy(r + 1, run->runs + index, ++ sizeof(struct ntfs_run) * (run->count - index)); ++ ++ ntfs_vfree(run->runs); ++ run->runs = new_ptr; ++ run->allocated = bytes; ++ ++ } else { ++ size_t i = run->count - index; ++ ++ r = run->runs + index; ++ ++ /* memmove appears to be a bottle neck here... */ ++ if (i > 0) ++ memmove(r + 1, r, sizeof(struct ntfs_run) * i); ++ } ++ ++ r->vcn = vcn; ++ r->lcn = lcn; ++ r->len = len; ++ run->count += 1; ++ } else { ++ r = run->runs + index; ++ ++ /* ++ * If one of ranges was not allocated ++ * then I have to split location I just matched. ++ * and insert current one ++ * a common case this requires tail to be reinserted ++ * a recursive call. ++ */ ++ if (((lcn == SPARSE_LCN) != (r->lcn == SPARSE_LCN)) || ++ (lcn != SPARSE_LCN && lcn != r->lcn + (vcn - r->vcn))) { ++ CLST to_eat = vcn - r->vcn; ++ CLST Tovcn = to_eat + len; ++ ++ should_add_tail = Tovcn < r->len; ++ ++ if (should_add_tail) { ++ tail_lcn = r->lcn == SPARSE_LCN ++ ? SPARSE_LCN ++ : (r->lcn + Tovcn); ++ tail_vcn = r->vcn + Tovcn; ++ tail_len = r->len - Tovcn; ++ } ++ ++ if (to_eat > 0) { ++ r->len = to_eat; ++ inrange = false; ++ index += 1; ++ goto requires_new_range; ++ } ++ ++ /* lcn should match one I'm going to add. */ ++ r->lcn = lcn; ++ } ++ ++ /* ++ * If existing range fits then I'm done. ++ * Otherwise extend found one and fall back to range jocode. ++ */ ++ if (r->vcn + r->len < vcn + len) ++ r->len += len - ((r->vcn + r->len) - vcn); ++ } ++ ++ /* ++ * And normalize it starting from insertion point. ++ * It's possible that no insertion needed case if ++ * start point lies within the range of an entry ++ * that 'index' points to. ++ */ ++ if (inrange && index > 0) ++ index -= 1; ++ run_consolidate(run, index); ++ run_consolidate(run, index + 1); ++ ++ /* ++ * a special case ++ * I have to add extra range a tail. ++ */ ++ if (should_add_tail && ++ !run_add_entry(run, tail_vcn, tail_lcn, tail_len, is_mft)) ++ return false; ++ ++ return true; ++} ++ ++/*helper for attr_collapse_range, which is helper for fallocate(collapse_range)*/ ++bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len) ++{ ++ size_t index, eat; ++ struct ntfs_run *r, *e, *eat_start, *eat_end; ++ CLST end; ++ ++ if (WARN_ON(!run_lookup(run, vcn, &index))) ++ return true; /* should never be here */ ++ ++ e = run->runs + run->count; ++ r = run->runs + index; ++ end = vcn + len; ++ ++ if (vcn > r->vcn) { ++ if (r->vcn + r->len <= end) { ++ /* collapse tail of run */ ++ r->len = vcn - r->vcn; ++ } else if (r->lcn == SPARSE_LCN) { ++ /* collapse a middle part of sparsed run */ ++ r->len -= len; ++ } else { ++ /* collapse a middle part of normal run, split */ ++ if (!run_add_entry(run, vcn, SPARSE_LCN, len, false)) ++ return false; ++ return run_collapse_range(run, vcn, len); ++ } ++ ++ r += 1; ++ } ++ ++ eat_start = r; ++ eat_end = r; ++ ++ for (; r < e; r++) { ++ CLST d; ++ ++ if (r->vcn >= end) { ++ r->vcn -= len; ++ continue; ++ } ++ ++ if (r->vcn + r->len <= end) { ++ /* eat this run */ ++ eat_end = r + 1; ++ continue; ++ } ++ ++ d = end - r->vcn; ++ if (r->lcn != SPARSE_LCN) ++ r->lcn += d; ++ r->len -= d; ++ r->vcn -= len - d; ++ } ++ ++ eat = eat_end - eat_start; ++ memmove(eat_start, eat_end, (e - eat_end) * sizeof(*r)); ++ run->count -= eat; ++ ++ return true; ++} ++ ++/* ++ * run_get_entry ++ * ++ * returns index-th mapped region ++ */ ++bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn, ++ CLST *lcn, CLST *len) ++{ ++ const struct ntfs_run *r; ++ ++ if (index >= run->count) ++ return false; ++ ++ r = run->runs + index; ++ ++ if (!r->len) ++ return false; ++ ++ if (vcn) ++ *vcn = r->vcn; ++ if (lcn) ++ *lcn = r->lcn; ++ if (len) ++ *len = r->len; ++ return true; ++} ++ ++/* ++ * run_packed_size ++ * ++ * calculates the size of packed int64 ++ */ ++#ifdef __BIG_ENDIAN ++static inline int run_packed_size(const s64 n) ++{ ++ const u8 *p = (const u8 *)&n + sizeof(n) - 1; ++ ++ if (n >= 0) { ++ if (p[-7] || p[-6] || p[-5] || p[-4]) ++ p -= 4; ++ if (p[-3] || p[-2]) ++ p -= 2; ++ if (p[-1]) ++ p -= 1; ++ if (p[0] & 0x80) ++ p -= 1; ++ } else { ++ if (p[-7] != 0xff || p[-6] != 0xff || p[-5] != 0xff || ++ p[-4] != 0xff) ++ p -= 4; ++ if (p[-3] != 0xff || p[-2] != 0xff) ++ p -= 2; ++ if (p[-1] != 0xff) ++ p -= 1; ++ if (!(p[0] & 0x80)) ++ p -= 1; ++ } ++ return (const u8 *)&n + sizeof(n) - p; ++} ++ ++/* full trusted function. It does not check 'size' for errors */ ++static inline void run_pack_s64(u8 *run_buf, u8 size, s64 v) ++{ ++ const u8 *p = (u8 *)&v; ++ ++ switch (size) { ++ case 8: ++ run_buf[7] = p[0]; ++ fallthrough; ++ case 7: ++ run_buf[6] = p[1]; ++ fallthrough; ++ case 6: ++ run_buf[5] = p[2]; ++ fallthrough; ++ case 5: ++ run_buf[4] = p[3]; ++ fallthrough; ++ case 4: ++ run_buf[3] = p[4]; ++ fallthrough; ++ case 3: ++ run_buf[2] = p[5]; ++ fallthrough; ++ case 2: ++ run_buf[1] = p[6]; ++ fallthrough; ++ case 1: ++ run_buf[0] = p[7]; ++ } ++} ++ ++/* full trusted function. It does not check 'size' for errors */ ++static inline s64 run_unpack_s64(const u8 *run_buf, u8 size, s64 v) ++{ ++ u8 *p = (u8 *)&v; ++ ++ switch (size) { ++ case 8: ++ p[0] = run_buf[7]; ++ fallthrough; ++ case 7: ++ p[1] = run_buf[6]; ++ fallthrough; ++ case 6: ++ p[2] = run_buf[5]; ++ fallthrough; ++ case 5: ++ p[3] = run_buf[4]; ++ fallthrough; ++ case 4: ++ p[4] = run_buf[3]; ++ fallthrough; ++ case 3: ++ p[5] = run_buf[2]; ++ fallthrough; ++ case 2: ++ p[6] = run_buf[1]; ++ fallthrough; ++ case 1: ++ p[7] = run_buf[0]; ++ } ++ return v; ++} ++ ++#else ++ ++static inline int run_packed_size(const s64 n) ++{ ++ const u8 *p = (const u8 *)&n; ++ ++ if (n >= 0) { ++ if (p[7] || p[6] || p[5] || p[4]) ++ p += 4; ++ if (p[3] || p[2]) ++ p += 2; ++ if (p[1]) ++ p += 1; ++ if (p[0] & 0x80) ++ p += 1; ++ } else { ++ if (p[7] != 0xff || p[6] != 0xff || p[5] != 0xff || ++ p[4] != 0xff) ++ p += 4; ++ if (p[3] != 0xff || p[2] != 0xff) ++ p += 2; ++ if (p[1] != 0xff) ++ p += 1; ++ if (!(p[0] & 0x80)) ++ p += 1; ++ } ++ ++ return 1 + p - (const u8 *)&n; ++} ++ ++/* full trusted function. It does not check 'size' for errors */ ++static inline void run_pack_s64(u8 *run_buf, u8 size, s64 v) ++{ ++ const u8 *p = (u8 *)&v; ++ ++ /* memcpy( run_buf, &v, size); is it faster? */ ++ switch (size) { ++ case 8: ++ run_buf[7] = p[7]; ++ fallthrough; ++ case 7: ++ run_buf[6] = p[6]; ++ fallthrough; ++ case 6: ++ run_buf[5] = p[5]; ++ fallthrough; ++ case 5: ++ run_buf[4] = p[4]; ++ fallthrough; ++ case 4: ++ run_buf[3] = p[3]; ++ fallthrough; ++ case 3: ++ run_buf[2] = p[2]; ++ fallthrough; ++ case 2: ++ run_buf[1] = p[1]; ++ fallthrough; ++ case 1: ++ run_buf[0] = p[0]; ++ } ++} ++ ++/* full trusted function. It does not check 'size' for errors */ ++static inline s64 run_unpack_s64(const u8 *run_buf, u8 size, s64 v) ++{ ++ u8 *p = (u8 *)&v; ++ ++ /* memcpy( &v, run_buf, size); is it faster? */ ++ switch (size) { ++ case 8: ++ p[7] = run_buf[7]; ++ fallthrough; ++ case 7: ++ p[6] = run_buf[6]; ++ fallthrough; ++ case 6: ++ p[5] = run_buf[5]; ++ fallthrough; ++ case 5: ++ p[4] = run_buf[4]; ++ fallthrough; ++ case 4: ++ p[3] = run_buf[3]; ++ fallthrough; ++ case 3: ++ p[2] = run_buf[2]; ++ fallthrough; ++ case 2: ++ p[1] = run_buf[1]; ++ fallthrough; ++ case 1: ++ p[0] = run_buf[0]; ++ } ++ return v; ++} ++#endif ++ ++/* ++ * run_pack ++ * ++ * packs runs into buffer ++ * packed_vcns - how much runs we have packed ++ * packed_size - how much bytes we have used run_buf ++ */ ++int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, ++ u32 run_buf_size, CLST *packed_vcns) ++{ ++ CLST next_vcn, vcn, lcn; ++ CLST prev_lcn = 0; ++ CLST evcn1 = svcn + len; ++ int packed_size = 0; ++ size_t i; ++ bool ok; ++ s64 dlcn; ++ int offset_size, size_size, tmp; ++ ++ next_vcn = vcn = svcn; ++ ++ *packed_vcns = 0; ++ ++ if (!len) ++ goto out; ++ ++ ok = run_lookup_entry(run, vcn, &lcn, &len, &i); ++ ++ if (!ok) ++ goto error; ++ ++ if (next_vcn != vcn) ++ goto error; ++ ++ for (;;) { ++ next_vcn = vcn + len; ++ if (next_vcn > evcn1) ++ len = evcn1 - vcn; ++ ++ /* how much bytes required to pack len */ ++ size_size = run_packed_size(len); ++ ++ /* offset_size - how much bytes is packed dlcn */ ++ if (lcn == SPARSE_LCN) { ++ offset_size = 0; ++ dlcn = 0; ++ } else { ++ /* NOTE: lcn can be less than prev_lcn! */ ++ dlcn = (s64)lcn - prev_lcn; ++ offset_size = run_packed_size(dlcn); ++ prev_lcn = lcn; ++ } ++ ++ tmp = run_buf_size - packed_size - 2 - offset_size; ++ if (tmp <= 0) ++ goto out; ++ ++ /* can we store this entire run */ ++ if (tmp < size_size) ++ goto out; ++ ++ if (run_buf) { ++ /* pack run header */ ++ run_buf[0] = ((u8)(size_size | (offset_size << 4))); ++ run_buf += 1; ++ ++ /* Pack the length of run */ ++ run_pack_s64(run_buf, size_size, len); ++ ++ run_buf += size_size; ++ /* Pack the offset from previous lcn */ ++ run_pack_s64(run_buf, offset_size, dlcn); ++ run_buf += offset_size; ++ } ++ ++ packed_size += 1 + offset_size + size_size; ++ *packed_vcns += len; ++ ++ if (packed_size + 1 >= run_buf_size || next_vcn >= evcn1) ++ goto out; ++ ++ ok = run_get_entry(run, ++i, &vcn, &lcn, &len); ++ if (!ok) ++ goto error; ++ ++ if (next_vcn != vcn) ++ goto error; ++ } ++ ++out: ++ /* Store last zero */ ++ if (run_buf) ++ run_buf[0] = 0; ++ ++ return packed_size + 1; ++ ++error: ++ return -EOPNOTSUPP; ++} ++ ++/* ++ * run_unpack ++ * ++ * unpacks packed runs from "run_buf" ++ * returns error, if negative, or real used bytes ++ */ ++int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, ++ CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf, ++ u32 run_buf_size) ++{ ++ u64 prev_lcn, vcn64, lcn, next_vcn; ++ const u8 *run_last, *run_0; ++ bool is_mft = ino == MFT_REC_MFT; ++ ++ /* Check for empty */ ++ if (evcn + 1 == svcn) ++ return 0; ++ ++ if (evcn < svcn) ++ return -EINVAL; ++ ++ run_0 = run_buf; ++ run_last = run_buf + run_buf_size; ++ prev_lcn = 0; ++ vcn64 = svcn; ++ ++ /* Read all runs the chain */ ++ /* size_size - how much bytes is packed len */ ++ while (run_buf < run_last) { ++ /* size_size - how much bytes is packed len */ ++ u8 size_size = *run_buf & 0xF; ++ /* offset_size - how much bytes is packed dlcn */ ++ u8 offset_size = *run_buf++ >> 4; ++ u64 len; ++ ++ if (!size_size) ++ break; ++ ++ /* ++ * Unpack runs. ++ * NOTE: runs are stored little endian order ++ * "len" is unsigned value, "dlcn" is signed ++ * Large positive number requires to store 5 bytes ++ * e.g.: 05 FF 7E FF FF 00 00 00 ++ */ ++ if (size_size > 8) ++ return -EINVAL; ++ ++ len = run_unpack_s64(run_buf, size_size, 0); ++ /* skip size_size */ ++ run_buf += size_size; ++ ++ if (!len) ++ return -EINVAL; ++ ++ if (!offset_size) ++ lcn = SPARSE_LCN64; ++ else if (offset_size <= 8) { ++ s64 dlcn; ++ ++ /* initial value of dlcn is -1 or 0 */ ++ dlcn = (run_buf[offset_size - 1] & 0x80) ? (s64)-1 : 0; ++ dlcn = run_unpack_s64(run_buf, offset_size, dlcn); ++ /* skip offset_size */ ++ run_buf += offset_size; ++ ++ if (!dlcn) ++ return -EINVAL; ++ lcn = prev_lcn + dlcn; ++ prev_lcn = lcn; ++ } else ++ return -EINVAL; ++ ++ next_vcn = vcn64 + len; ++ /* check boundary */ ++ if (next_vcn > evcn + 1) ++ return -EINVAL; ++ ++#ifndef CONFIG_NTFS3_64BIT_CLUSTER ++ if (next_vcn > 0x100000000ull || (lcn + len) > 0x100000000ull) { ++ ntfs_err( ++ sbi->sb, ++ "This driver is compiled whitout CONFIG_NTFS3_64BIT_CLUSTER (like windows driver).\n" ++ "Volume contains 64 bits run: vcn %llx, lcn %llx, len %llx.\n" ++ "Activate CONFIG_NTFS3_64BIT_CLUSTER to process this case", ++ vcn64, lcn, len); ++ return -EOPNOTSUPP; ++ } ++#endif ++ if (lcn != SPARSE_LCN64 && lcn + len > sbi->used.bitmap.nbits) { ++ /* lcn range is out of volume */ ++ return -EINVAL; ++ } ++ ++ if (!run) ++ ; /* called from check_attr(fslog.c) to check run */ ++ else if (run == RUN_DEALLOCATE) { ++ /* called from ni_delete_all to free clusters without storing in run */ ++ if (lcn != SPARSE_LCN64) ++ mark_as_free_ex(sbi, lcn, len, true); ++ } else if (vcn64 >= vcn) { ++ if (!run_add_entry(run, vcn64, lcn, len, is_mft)) ++ return -ENOMEM; ++ } else if (next_vcn > vcn) { ++ u64 dlen = vcn - vcn64; ++ ++ if (!run_add_entry(run, vcn, lcn + dlen, len - dlen, ++ is_mft)) ++ return -ENOMEM; ++ } ++ ++ vcn64 = next_vcn; ++ } ++ ++ if (vcn64 != evcn + 1) { ++ /* not expected length of unpacked runs */ ++ return -EINVAL; ++ } ++ ++ return run_buf - run_0; ++} ++ ++#ifdef NTFS3_CHECK_FREE_CLST ++/* ++ * run_unpack_ex ++ * ++ * unpacks packed runs from "run_buf" ++ * checks unpacked runs to be used in bitmap ++ * returns error, if negative, or real used bytes ++ */ ++int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, ++ CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf, ++ u32 run_buf_size) ++{ ++ int ret, err; ++ CLST next_vcn, lcn, len; ++ size_t index; ++ bool ok; ++ struct wnd_bitmap *wnd; ++ ++ ret = run_unpack(run, sbi, ino, svcn, evcn, vcn, run_buf, run_buf_size); ++ if (ret <= 0) ++ return ret; ++ ++ if (!sbi->used.bitmap.sb || !run || run == RUN_DEALLOCATE) ++ return ret; ++ ++ if (ino == MFT_REC_BADCLUST) ++ return ret; ++ ++ next_vcn = vcn = svcn; ++ wnd = &sbi->used.bitmap; ++ ++ for (ok = run_lookup_entry(run, vcn, &lcn, &len, &index); ++ next_vcn <= evcn; ++ ok = run_get_entry(run, ++index, &vcn, &lcn, &len)) { ++ if (!ok || next_vcn != vcn) ++ return -EINVAL; ++ ++ next_vcn = vcn + len; ++ ++ if (lcn == SPARSE_LCN) ++ continue; ++ ++ if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) ++ continue; ++ ++ down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); ++ /* Check for free blocks */ ++ ok = wnd_is_used(wnd, lcn, len); ++ up_read(&wnd->rw_lock); ++ if (ok) ++ continue; ++ ++ /* Looks like volume is corrupted */ ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ ++ if (down_write_trylock(&wnd->rw_lock)) { ++ /* mark all zero bits as used in range [lcn, lcn+len) */ ++ CLST i, lcn_f = 0, len_f = 0; ++ ++ err = 0; ++ for (i = 0; i < len; i++) { ++ if (wnd_is_free(wnd, lcn + i, 1)) { ++ if (!len_f) ++ lcn_f = lcn + i; ++ len_f += 1; ++ } else if (len_f) { ++ err = wnd_set_used(wnd, lcn_f, len_f); ++ len_f = 0; ++ if (err) ++ break; ++ } ++ } ++ ++ if (len_f) ++ err = wnd_set_used(wnd, lcn_f, len_f); ++ ++ up_write(&wnd->rw_lock); ++ if (err) ++ return err; ++ } ++ } ++ ++ return ret; ++} ++#endif ++ ++/* ++ * run_get_highest_vcn ++ * ++ * returns the highest vcn from a mapping pairs array ++ * it used while replaying log file ++ */ ++int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) ++{ ++ u64 vcn64 = vcn; ++ u8 size_size; ++ ++ while ((size_size = *run_buf & 0xF)) { ++ u8 offset_size = *run_buf++ >> 4; ++ u64 len; ++ ++ if (size_size > 8 || offset_size > 8) ++ return -EINVAL; ++ ++ len = run_unpack_s64(run_buf, size_size, 0); ++ if (!len) ++ return -EINVAL; ++ ++ run_buf += size_size + offset_size; ++ vcn64 += len; ++ ++#ifndef CONFIG_NTFS3_64BIT_CLUSTER ++ if (vcn64 > 0x100000000ull) ++ return -EINVAL; ++#endif ++ } ++ ++ *highest_vcn = vcn64 - 1; ++ return 0; ++} +-- +2.31.1 + diff --git a/SOURCES/0005-fs-ntfs3-Add-attrib-operations.patch b/SOURCES/0005-fs-ntfs3-Add-attrib-operations.patch new file mode 100644 index 0000000..140d3d9 --- /dev/null +++ b/SOURCES/0005-fs-ntfs3-Add-attrib-operations.patch @@ -0,0 +1,3718 @@ +From be71b5cba2e6485e8959da7a9f9a44461a1bb074 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:30 +0300 +Subject: [Backport be71b5cba2e6] src: Add attrib operations + +This adds attrib operations + +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 2096 +++++++++++++++++++++++++++++++++++++++++++ + src/attrlist.c | 456 ++++++++++ + src/xattr.c | 1128 +++++++++++++++++++++++ + 3 files changed, 3680 insertions(+) + create mode 100644 src/attrib.c + create mode 100644 src/attrlist.c + create mode 100644 src/xattr.c + +diff --git a/src/attrib.c b/src/attrib.c +new file mode 100644 +index 0000000000000000000000000000000000000000..046dc57f75f2169b47e7078c251f26b05ec69b42 +--- /dev/null ++++ b/src/attrib.c +@@ -0,0 +1,2096 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ * TODO: merge attr_set_size/attr_data_get_block/attr_allocate_frame? ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* ++ * You can set external NTFS_MIN_LOG2_OF_CLUMP/NTFS_MAX_LOG2_OF_CLUMP to manage ++ * preallocate algorithm ++ */ ++#ifndef NTFS_MIN_LOG2_OF_CLUMP ++#define NTFS_MIN_LOG2_OF_CLUMP 16 ++#endif ++ ++#ifndef NTFS_MAX_LOG2_OF_CLUMP ++#define NTFS_MAX_LOG2_OF_CLUMP 26 ++#endif ++ ++// 16M ++#define NTFS_CLUMP_MIN (1 << (NTFS_MIN_LOG2_OF_CLUMP + 8)) ++// 16G ++#define NTFS_CLUMP_MAX (1ull << (NTFS_MAX_LOG2_OF_CLUMP + 8)) ++ ++/* ++ * get_pre_allocated ++ * ++ */ ++static inline u64 get_pre_allocated(u64 size) ++{ ++ u32 clump; ++ u8 align_shift; ++ u64 ret; ++ ++ if (size <= NTFS_CLUMP_MIN) { ++ clump = 1 << NTFS_MIN_LOG2_OF_CLUMP; ++ align_shift = NTFS_MIN_LOG2_OF_CLUMP; ++ } else if (size >= NTFS_CLUMP_MAX) { ++ clump = 1 << NTFS_MAX_LOG2_OF_CLUMP; ++ align_shift = NTFS_MAX_LOG2_OF_CLUMP; ++ } else { ++ align_shift = NTFS_MIN_LOG2_OF_CLUMP - 1 + ++ __ffs(size >> (8 + NTFS_MIN_LOG2_OF_CLUMP)); ++ clump = 1u << align_shift; ++ } ++ ++ ret = (((size + clump - 1) >> align_shift)) << align_shift; ++ ++ return ret; ++} ++ ++/* ++ * attr_must_be_resident ++ * ++ * returns true if attribute must be resident ++ */ ++static inline bool attr_must_be_resident(struct ntfs_sb_info *sbi, ++ enum ATTR_TYPE type) ++{ ++ const struct ATTR_DEF_ENTRY *de; ++ ++ switch (type) { ++ case ATTR_STD: ++ case ATTR_NAME: ++ case ATTR_ID: ++ case ATTR_LABEL: ++ case ATTR_VOL_INFO: ++ case ATTR_ROOT: ++ case ATTR_EA_INFO: ++ return true; ++ default: ++ de = ntfs_query_def(sbi, type); ++ if (de && (de->flags & NTFS_ATTR_MUST_BE_RESIDENT)) ++ return true; ++ return false; ++ } ++} ++ ++/* ++ * attr_load_runs ++ * ++ * load all runs stored in 'attr' ++ */ ++int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni, ++ struct runs_tree *run, const CLST *vcn) ++{ ++ int err; ++ CLST svcn = le64_to_cpu(attr->nres.svcn); ++ CLST evcn = le64_to_cpu(attr->nres.evcn); ++ u32 asize; ++ u16 run_off; ++ ++ if (svcn >= evcn + 1 || run_is_mapped_full(run, svcn, evcn)) ++ return 0; ++ ++ if (vcn && (evcn < *vcn || *vcn < svcn)) ++ return -EINVAL; ++ ++ asize = le32_to_cpu(attr->size); ++ run_off = le16_to_cpu(attr->nres.run_off); ++ err = run_unpack_ex(run, ni->mi.sbi, ni->mi.rno, svcn, evcn, ++ vcn ? *vcn : svcn, Add2Ptr(attr, run_off), ++ asize - run_off); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++/* ++ * int run_deallocate_ex ++ * ++ * Deallocate clusters ++ */ ++static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run, ++ CLST vcn, CLST len, CLST *done, bool trim) ++{ ++ int err = 0; ++ CLST vcn_next, vcn0 = vcn, lcn, clen, dn = 0; ++ size_t idx; ++ ++ if (!len) ++ goto out; ++ ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { ++failed: ++ run_truncate(run, vcn0); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ for (;;) { ++ if (clen > len) ++ clen = len; ++ ++ if (!clen) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (lcn != SPARSE_LCN) { ++ mark_as_free_ex(sbi, lcn, clen, trim); ++ dn += clen; ++ } ++ ++ len -= clen; ++ if (!len) ++ break; ++ ++ vcn_next = vcn + clen; ++ if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || ++ vcn != vcn_next) { ++ // save memory - don't load entire run ++ goto failed; ++ } ++ } ++ ++out: ++ if (done) ++ *done += dn; ++ ++ return err; ++} ++ ++/* ++ * attr_allocate_clusters ++ * ++ * find free space, mark it as used and store in 'run' ++ */ ++int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, ++ CLST vcn, CLST lcn, CLST len, CLST *pre_alloc, ++ enum ALLOCATE_OPT opt, CLST *alen, const size_t fr, ++ CLST *new_lcn) ++{ ++ int err; ++ CLST flen, vcn0 = vcn, pre = pre_alloc ? *pre_alloc : 0; ++ struct wnd_bitmap *wnd = &sbi->used.bitmap; ++ size_t cnt = run->count; ++ ++ for (;;) { ++ err = ntfs_look_for_free_space(sbi, lcn, len + pre, &lcn, &flen, ++ opt); ++ ++ if (err == -ENOSPC && pre) { ++ pre = 0; ++ if (*pre_alloc) ++ *pre_alloc = 0; ++ continue; ++ } ++ ++ if (err) ++ goto out; ++ ++ if (new_lcn && vcn == vcn0) ++ *new_lcn = lcn; ++ ++ /* Add new fragment into run storage */ ++ if (!run_add_entry(run, vcn, lcn, flen, opt == ALLOCATE_MFT)) { ++ down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); ++ wnd_set_free(wnd, lcn, flen); ++ up_write(&wnd->rw_lock); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ vcn += flen; ++ ++ if (flen >= len || opt == ALLOCATE_MFT || ++ (fr && run->count - cnt >= fr)) { ++ *alen = vcn - vcn0; ++ return 0; ++ } ++ ++ len -= flen; ++ } ++ ++out: ++ /* undo */ ++ run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false); ++ run_truncate(run, vcn0); ++ ++ return err; ++} ++ ++/* ++ * if page is not NULL - it is already contains resident data ++ * and locked (called from ni_write_frame) ++ */ ++int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY *le, struct mft_inode *mi, ++ u64 new_size, struct runs_tree *run, ++ struct ATTRIB **ins_attr, struct page *page) ++{ ++ struct ntfs_sb_info *sbi; ++ struct ATTRIB *attr_s; ++ struct MFT_REC *rec; ++ u32 used, asize, rsize, aoff, align; ++ bool is_data; ++ CLST len, alen; ++ char *next; ++ int err; ++ ++ if (attr->non_res) { ++ *ins_attr = attr; ++ return 0; ++ } ++ ++ sbi = mi->sbi; ++ rec = mi->mrec; ++ attr_s = NULL; ++ used = le32_to_cpu(rec->used); ++ asize = le32_to_cpu(attr->size); ++ next = Add2Ptr(attr, asize); ++ aoff = PtrOffset(rec, attr); ++ rsize = le32_to_cpu(attr->res.data_size); ++ is_data = attr->type == ATTR_DATA && !attr->name_len; ++ ++ align = sbi->cluster_size; ++ if (is_attr_compressed(attr)) ++ align <<= COMPRESSION_UNIT; ++ len = (rsize + align - 1) >> sbi->cluster_bits; ++ ++ run_init(run); ++ ++ /* make a copy of original attribute */ ++ attr_s = ntfs_memdup(attr, asize); ++ if (!attr_s) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (!len) { ++ /* empty resident -> empty nonresident */ ++ alen = 0; ++ } else { ++ const char *data = resident_data(attr); ++ ++ err = attr_allocate_clusters(sbi, run, 0, 0, len, NULL, ++ ALLOCATE_DEF, &alen, 0, NULL); ++ if (err) ++ goto out1; ++ ++ if (!rsize) { ++ /* empty resident -> non empty nonresident */ ++ } else if (!is_data) { ++ err = ntfs_sb_write_run(sbi, run, 0, data, rsize); ++ if (err) ++ goto out2; ++ } else if (!page) { ++ char *kaddr; ++ ++ page = grab_cache_page(ni->vfs_inode.i_mapping, 0); ++ if (!page) { ++ err = -ENOMEM; ++ goto out2; ++ } ++ kaddr = kmap_atomic(page); ++ memcpy(kaddr, data, rsize); ++ memset(kaddr + rsize, 0, PAGE_SIZE - rsize); ++ kunmap_atomic(kaddr); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ set_page_dirty(page); ++ unlock_page(page); ++ put_page(page); ++ } ++ } ++ ++ /* remove original attribute */ ++ used -= asize; ++ memmove(attr, Add2Ptr(attr, asize), used - aoff); ++ rec->used = cpu_to_le32(used); ++ mi->dirty = true; ++ if (le) ++ al_remove_le(ni, le); ++ ++ err = ni_insert_nonresident(ni, attr_s->type, attr_name(attr_s), ++ attr_s->name_len, run, 0, alen, ++ attr_s->flags, &attr, NULL); ++ if (err) ++ goto out3; ++ ++ ntfs_free(attr_s); ++ attr->nres.data_size = cpu_to_le64(rsize); ++ attr->nres.valid_size = attr->nres.data_size; ++ ++ *ins_attr = attr; ++ ++ if (is_data) ++ ni->ni_flags &= ~NI_FLAG_RESIDENT; ++ ++ /* Resident attribute becomes non resident */ ++ return 0; ++ ++out3: ++ attr = Add2Ptr(rec, aoff); ++ memmove(next, attr, used - aoff); ++ memcpy(attr, attr_s, asize); ++ rec->used = cpu_to_le32(used + asize); ++ mi->dirty = true; ++out2: ++ /* undo: do not trim new allocated clusters */ ++ run_deallocate(sbi, run, false); ++ run_close(run); ++out1: ++ ntfs_free(attr_s); ++ /*reinsert le*/ ++out: ++ return err; ++} ++ ++/* ++ * attr_set_size_res ++ * ++ * helper for attr_set_size ++ */ ++static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct ATTR_LIST_ENTRY *le, struct mft_inode *mi, ++ u64 new_size, struct runs_tree *run, ++ struct ATTRIB **ins_attr) ++{ ++ struct ntfs_sb_info *sbi = mi->sbi; ++ struct MFT_REC *rec = mi->mrec; ++ u32 used = le32_to_cpu(rec->used); ++ u32 asize = le32_to_cpu(attr->size); ++ u32 aoff = PtrOffset(rec, attr); ++ u32 rsize = le32_to_cpu(attr->res.data_size); ++ u32 tail = used - aoff - asize; ++ char *next = Add2Ptr(attr, asize); ++ s64 dsize = QuadAlign(new_size) - QuadAlign(rsize); ++ ++ if (dsize < 0) { ++ memmove(next + dsize, next, tail); ++ } else if (dsize > 0) { ++ if (used + dsize > sbi->max_bytes_per_attr) ++ return attr_make_nonresident(ni, attr, le, mi, new_size, ++ run, ins_attr, NULL); ++ ++ memmove(next + dsize, next, tail); ++ memset(next, 0, dsize); ++ } ++ ++ if (new_size > rsize) ++ memset(Add2Ptr(resident_data(attr), rsize), 0, ++ new_size - rsize); ++ ++ rec->used = cpu_to_le32(used + dsize); ++ attr->size = cpu_to_le32(asize + dsize); ++ attr->res.data_size = cpu_to_le32(new_size); ++ mi->dirty = true; ++ *ins_attr = attr; ++ ++ return 0; ++} ++ ++/* ++ * attr_set_size ++ * ++ * change the size of attribute ++ * Extend: ++ * - sparse/compressed: no allocated clusters ++ * - normal: append allocated and preallocated new clusters ++ * Shrink: ++ * - no deallocate if keep_prealloc is set ++ */ ++int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, struct runs_tree *run, ++ u64 new_size, const u64 *new_valid, bool keep_prealloc, ++ struct ATTRIB **ret) ++{ ++ int err = 0; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ u8 cluster_bits = sbi->cluster_bits; ++ bool is_mft = ++ ni->mi.rno == MFT_REC_MFT && type == ATTR_DATA && !name_len; ++ u64 old_valid, old_size, old_alloc, new_alloc, new_alloc_tmp; ++ struct ATTRIB *attr = NULL, *attr_b; ++ struct ATTR_LIST_ENTRY *le, *le_b; ++ struct mft_inode *mi, *mi_b; ++ CLST alen, vcn, lcn, new_alen, old_alen, svcn, evcn; ++ CLST next_svcn, pre_alloc = -1, done = 0; ++ bool is_ext; ++ u32 align; ++ struct MFT_REC *rec; ++ ++again: ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, type, name, name_len, NULL, ++ &mi_b); ++ if (!attr_b) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ if (!attr_b->non_res) { ++ err = attr_set_size_res(ni, attr_b, le_b, mi_b, new_size, run, ++ &attr_b); ++ if (err || !attr_b->non_res) ++ goto out; ++ ++ /* layout of records may be changed, so do a full search */ ++ goto again; ++ } ++ ++ is_ext = is_attr_ext(attr_b); ++ ++again_1: ++ align = sbi->cluster_size; ++ ++ if (is_ext) { ++ align <<= attr_b->nres.c_unit; ++ if (is_attr_sparsed(attr_b)) ++ keep_prealloc = false; ++ } ++ ++ old_valid = le64_to_cpu(attr_b->nres.valid_size); ++ old_size = le64_to_cpu(attr_b->nres.data_size); ++ old_alloc = le64_to_cpu(attr_b->nres.alloc_size); ++ old_alen = old_alloc >> cluster_bits; ++ ++ new_alloc = (new_size + align - 1) & ~(u64)(align - 1); ++ new_alen = new_alloc >> cluster_bits; ++ ++ if (keep_prealloc && is_ext) ++ keep_prealloc = false; ++ ++ if (keep_prealloc && new_size < old_size) { ++ attr_b->nres.data_size = cpu_to_le64(new_size); ++ mi_b->dirty = true; ++ goto ok; ++ } ++ ++ vcn = old_alen - 1; ++ ++ svcn = le64_to_cpu(attr_b->nres.svcn); ++ evcn = le64_to_cpu(attr_b->nres.evcn); ++ ++ if (svcn <= vcn && vcn <= evcn) { ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ } else if (!le_b) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ le = le_b; ++ attr = ni_find_attr(ni, attr_b, &le, type, name, name_len, &vcn, ++ &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++next_le_1: ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ } ++ ++next_le: ++ rec = mi->mrec; ++ ++ err = attr_load_runs(attr, ni, run, NULL); ++ if (err) ++ goto out; ++ ++ if (new_size > old_size) { ++ CLST to_allocate; ++ size_t free; ++ ++ if (new_alloc <= old_alloc) { ++ attr_b->nres.data_size = cpu_to_le64(new_size); ++ mi_b->dirty = true; ++ goto ok; ++ } ++ ++ to_allocate = new_alen - old_alen; ++add_alloc_in_same_attr_seg: ++ lcn = 0; ++ if (is_mft) { ++ /* mft allocates clusters from mftzone */ ++ pre_alloc = 0; ++ } else if (is_ext) { ++ /* no preallocate for sparse/compress */ ++ pre_alloc = 0; ++ } else if (pre_alloc == -1) { ++ pre_alloc = 0; ++ if (type == ATTR_DATA && !name_len && ++ sbi->options.prealloc) { ++ CLST new_alen2 = bytes_to_cluster( ++ sbi, get_pre_allocated(new_size)); ++ pre_alloc = new_alen2 - new_alen; ++ } ++ ++ /* Get the last lcn to allocate from */ ++ if (old_alen && ++ !run_lookup_entry(run, vcn, &lcn, NULL, NULL)) { ++ lcn = SPARSE_LCN; ++ } ++ ++ if (lcn == SPARSE_LCN) ++ lcn = 0; ++ else if (lcn) ++ lcn += 1; ++ ++ free = wnd_zeroes(&sbi->used.bitmap); ++ if (to_allocate > free) { ++ err = -ENOSPC; ++ goto out; ++ } ++ ++ if (pre_alloc && to_allocate + pre_alloc > free) ++ pre_alloc = 0; ++ } ++ ++ vcn = old_alen; ++ ++ if (is_ext) { ++ if (!run_add_entry(run, vcn, SPARSE_LCN, to_allocate, ++ false)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ alen = to_allocate; ++ } else { ++ /* ~3 bytes per fragment */ ++ err = attr_allocate_clusters( ++ sbi, run, vcn, lcn, to_allocate, &pre_alloc, ++ is_mft ? ALLOCATE_MFT : 0, &alen, ++ is_mft ? 0 ++ : (sbi->record_size - ++ le32_to_cpu(rec->used) + 8) / ++ 3 + ++ 1, ++ NULL); ++ if (err) ++ goto out; ++ } ++ ++ done += alen; ++ vcn += alen; ++ if (to_allocate > alen) ++ to_allocate -= alen; ++ else ++ to_allocate = 0; ++ ++pack_runs: ++ err = mi_pack_runs(mi, attr, run, vcn - svcn); ++ if (err) ++ goto out; ++ ++ next_svcn = le64_to_cpu(attr->nres.evcn) + 1; ++ new_alloc_tmp = (u64)next_svcn << cluster_bits; ++ attr_b->nres.alloc_size = cpu_to_le64(new_alloc_tmp); ++ mi_b->dirty = true; ++ ++ if (next_svcn >= vcn && !to_allocate) { ++ /* Normal way. update attribute and exit */ ++ attr_b->nres.data_size = cpu_to_le64(new_size); ++ goto ok; ++ } ++ ++ /* at least two mft to avoid recursive loop*/ ++ if (is_mft && next_svcn == vcn && ++ ((u64)done << sbi->cluster_bits) >= 2 * sbi->record_size) { ++ new_size = new_alloc_tmp; ++ attr_b->nres.data_size = attr_b->nres.alloc_size; ++ goto ok; ++ } ++ ++ if (le32_to_cpu(rec->used) < sbi->record_size) { ++ old_alen = next_svcn; ++ evcn = old_alen - 1; ++ goto add_alloc_in_same_attr_seg; ++ } ++ ++ attr_b->nres.data_size = attr_b->nres.alloc_size; ++ if (new_alloc_tmp < old_valid) ++ attr_b->nres.valid_size = attr_b->nres.data_size; ++ ++ if (type == ATTR_LIST) { ++ err = ni_expand_list(ni); ++ if (err) ++ goto out; ++ if (next_svcn < vcn) ++ goto pack_runs; ++ ++ /* layout of records is changed */ ++ goto again; ++ } ++ ++ if (!ni->attr_list.size) { ++ err = ni_create_attr_list(ni); ++ if (err) ++ goto out; ++ /* layout of records is changed */ ++ } ++ ++ if (next_svcn >= vcn) { ++ /* this is mft data, repeat */ ++ goto again; ++ } ++ ++ /* insert new attribute segment */ ++ err = ni_insert_nonresident(ni, type, name, name_len, run, ++ next_svcn, vcn - next_svcn, ++ attr_b->flags, &attr, &mi); ++ if (err) ++ goto out; ++ ++ if (!is_mft) ++ run_truncate_head(run, evcn + 1); ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ ++ le_b = NULL; ++ /* layout of records maybe changed */ ++ /* find base attribute to update*/ ++ attr_b = ni_find_attr(ni, NULL, &le_b, type, name, name_len, ++ NULL, &mi_b); ++ if (!attr_b) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ attr_b->nres.alloc_size = cpu_to_le64((u64)vcn << cluster_bits); ++ attr_b->nres.data_size = attr_b->nres.alloc_size; ++ attr_b->nres.valid_size = attr_b->nres.alloc_size; ++ mi_b->dirty = true; ++ goto again_1; ++ } ++ ++ if (new_size != old_size || ++ (new_alloc != old_alloc && !keep_prealloc)) { ++ vcn = max(svcn, new_alen); ++ new_alloc_tmp = (u64)vcn << cluster_bits; ++ ++ alen = 0; ++ err = run_deallocate_ex(sbi, run, vcn, evcn - vcn + 1, &alen, ++ true); ++ if (err) ++ goto out; ++ ++ run_truncate(run, vcn); ++ ++ if (vcn > svcn) { ++ err = mi_pack_runs(mi, attr, run, vcn - svcn); ++ if (err) ++ goto out; ++ } else if (le && le->vcn) { ++ u16 le_sz = le16_to_cpu(le->size); ++ ++ /* ++ * NOTE: list entries for one attribute are always ++ * the same size. We deal with last entry (vcn==0) ++ * and it is not first in entries array ++ * (list entry for std attribute always first) ++ * So it is safe to step back ++ */ ++ mi_remove_attr(mi, attr); ++ ++ if (!al_remove_le(ni, le)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ le = (struct ATTR_LIST_ENTRY *)((u8 *)le - le_sz); ++ } else { ++ attr->nres.evcn = cpu_to_le64((u64)vcn - 1); ++ mi->dirty = true; ++ } ++ ++ attr_b->nres.alloc_size = cpu_to_le64(new_alloc_tmp); ++ ++ if (vcn == new_alen) { ++ attr_b->nres.data_size = cpu_to_le64(new_size); ++ if (new_size < old_valid) ++ attr_b->nres.valid_size = ++ attr_b->nres.data_size; ++ } else { ++ if (new_alloc_tmp <= ++ le64_to_cpu(attr_b->nres.data_size)) ++ attr_b->nres.data_size = ++ attr_b->nres.alloc_size; ++ if (new_alloc_tmp < ++ le64_to_cpu(attr_b->nres.valid_size)) ++ attr_b->nres.valid_size = ++ attr_b->nres.alloc_size; ++ } ++ ++ if (is_ext) ++ le64_sub_cpu(&attr_b->nres.total_size, ++ ((u64)alen << cluster_bits)); ++ ++ mi_b->dirty = true; ++ ++ if (new_alloc_tmp <= new_alloc) ++ goto ok; ++ ++ old_size = new_alloc_tmp; ++ vcn = svcn - 1; ++ ++ if (le == le_b) { ++ attr = attr_b; ++ mi = mi_b; ++ evcn = svcn - 1; ++ svcn = 0; ++ goto next_le; ++ } ++ ++ if (le->type != type || le->name_len != name_len || ++ memcmp(le_name(le), name, name_len * sizeof(short))) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = ni_load_mi(ni, le, &mi); ++ if (err) ++ goto out; ++ ++ attr = mi_find_attr(mi, NULL, type, name, name_len, &le->id); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ goto next_le_1; ++ } ++ ++ok: ++ if (new_valid) { ++ __le64 valid = cpu_to_le64(min(*new_valid, new_size)); ++ ++ if (attr_b->nres.valid_size != valid) { ++ attr_b->nres.valid_size = valid; ++ mi_b->dirty = true; ++ } ++ } ++ ++out: ++ if (!err && attr_b && ret) ++ *ret = attr_b; ++ ++ /* update inode_set_bytes*/ ++ if (!err && ((type == ATTR_DATA && !name_len) || ++ (type == ATTR_ALLOC && name == I30_NAME))) { ++ bool dirty = false; ++ ++ if (ni->vfs_inode.i_size != new_size) { ++ ni->vfs_inode.i_size = new_size; ++ dirty = true; ++ } ++ ++ if (attr_b && attr_b->non_res) { ++ new_alloc = le64_to_cpu(attr_b->nres.alloc_size); ++ if (inode_get_bytes(&ni->vfs_inode) != new_alloc) { ++ inode_set_bytes(&ni->vfs_inode, new_alloc); ++ dirty = true; ++ } ++ } ++ ++ if (dirty) { ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ mark_inode_dirty(&ni->vfs_inode); ++ } ++ } ++ ++ return err; ++} ++ ++int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, ++ CLST *len, bool *new) ++{ ++ int err = 0; ++ struct runs_tree *run = &ni->file.run; ++ struct ntfs_sb_info *sbi; ++ u8 cluster_bits; ++ struct ATTRIB *attr = NULL, *attr_b; ++ struct ATTR_LIST_ENTRY *le, *le_b; ++ struct mft_inode *mi, *mi_b; ++ CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end; ++ u64 total_size; ++ u32 clst_per_frame; ++ bool ok; ++ ++ if (new) ++ *new = false; ++ ++ down_read(&ni->file.run_lock); ++ ok = run_lookup_entry(run, vcn, lcn, len, NULL); ++ up_read(&ni->file.run_lock); ++ ++ if (ok && (*lcn != SPARSE_LCN || !new)) { ++ /* normal way */ ++ return 0; ++ } ++ ++ if (!clen) ++ clen = 1; ++ ++ if (ok && clen > *len) ++ clen = *len; ++ ++ sbi = ni->mi.sbi; ++ cluster_bits = sbi->cluster_bits; ++ ++ ni_lock(ni); ++ down_write(&ni->file.run_lock); ++ ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); ++ if (!attr_b) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ if (!attr_b->non_res) { ++ *lcn = RESIDENT_LCN; ++ *len = 1; ++ goto out; ++ } ++ ++ asize = le64_to_cpu(attr_b->nres.alloc_size) >> sbi->cluster_bits; ++ if (vcn >= asize) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ clst_per_frame = 1u << attr_b->nres.c_unit; ++ to_alloc = (clen + clst_per_frame - 1) & ~(clst_per_frame - 1); ++ ++ if (vcn + to_alloc > asize) ++ to_alloc = asize - vcn; ++ ++ svcn = le64_to_cpu(attr_b->nres.svcn); ++ evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1; ++ ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ ++ if (le_b && (vcn < svcn || evcn1 <= vcn)) { ++ attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, ++ &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn1 = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ ++ err = attr_load_runs(attr, ni, run, NULL); ++ if (err) ++ goto out; ++ ++ if (!ok) { ++ ok = run_lookup_entry(run, vcn, lcn, len, NULL); ++ if (ok && (*lcn != SPARSE_LCN || !new)) { ++ /* normal way */ ++ err = 0; ++ goto ok; ++ } ++ ++ if (!ok && !new) { ++ *len = 0; ++ err = 0; ++ goto ok; ++ } ++ ++ if (ok && clen > *len) { ++ clen = *len; ++ to_alloc = (clen + clst_per_frame - 1) & ++ ~(clst_per_frame - 1); ++ } ++ } ++ ++ if (!is_attr_ext(attr_b)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Get the last lcn to allocate from */ ++ hint = 0; ++ ++ if (vcn > evcn1) { ++ if (!run_add_entry(run, evcn1, SPARSE_LCN, vcn - evcn1, ++ false)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } else if (vcn && !run_lookup_entry(run, vcn - 1, &hint, NULL, NULL)) { ++ hint = -1; ++ } ++ ++ err = attr_allocate_clusters( ++ sbi, run, vcn, hint + 1, to_alloc, NULL, 0, len, ++ (sbi->record_size - le32_to_cpu(mi->mrec->used) + 8) / 3 + 1, ++ lcn); ++ if (err) ++ goto out; ++ *new = true; ++ ++ end = vcn + *len; ++ ++ total_size = le64_to_cpu(attr_b->nres.total_size) + ++ ((u64)*len << cluster_bits); ++ ++repack: ++ err = mi_pack_runs(mi, attr, run, max(end, evcn1) - svcn); ++ if (err) ++ goto out; ++ ++ attr_b->nres.total_size = cpu_to_le64(total_size); ++ inode_set_bytes(&ni->vfs_inode, total_size); ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ ++ mi_b->dirty = true; ++ mark_inode_dirty(&ni->vfs_inode); ++ ++ /* stored [vcn : next_svcn) from [vcn : end) */ ++ next_svcn = le64_to_cpu(attr->nres.evcn) + 1; ++ ++ if (end <= evcn1) { ++ if (next_svcn == evcn1) { ++ /* Normal way. update attribute and exit */ ++ goto ok; ++ } ++ /* add new segment [next_svcn : evcn1 - next_svcn )*/ ++ if (!ni->attr_list.size) { ++ err = ni_create_attr_list(ni); ++ if (err) ++ goto out; ++ /* layout of records is changed */ ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, ++ 0, NULL, &mi_b); ++ if (!attr_b) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ goto repack; ++ } ++ } ++ ++ svcn = evcn1; ++ ++ /* Estimate next attribute */ ++ attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); ++ ++ if (attr) { ++ CLST alloc = bytes_to_cluster( ++ sbi, le64_to_cpu(attr_b->nres.alloc_size)); ++ CLST evcn = le64_to_cpu(attr->nres.evcn); ++ ++ if (end < next_svcn) ++ end = next_svcn; ++ while (end > evcn) { ++ /* remove segment [svcn : evcn)*/ ++ mi_remove_attr(mi, attr); ++ ++ if (!al_remove_le(ni, le)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (evcn + 1 >= alloc) { ++ /* last attribute segment */ ++ evcn1 = evcn + 1; ++ goto ins_ext; ++ } ++ ++ if (ni_load_mi(ni, le, &mi)) { ++ attr = NULL; ++ goto out; ++ } ++ ++ attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, ++ &le->id); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ } ++ ++ if (end < svcn) ++ end = svcn; ++ ++ err = attr_load_runs(attr, ni, run, &end); ++ if (err) ++ goto out; ++ ++ evcn1 = evcn + 1; ++ attr->nres.svcn = cpu_to_le64(next_svcn); ++ err = mi_pack_runs(mi, attr, run, evcn1 - next_svcn); ++ if (err) ++ goto out; ++ ++ le->vcn = cpu_to_le64(next_svcn); ++ ni->attr_list.dirty = true; ++ mi->dirty = true; ++ ++ next_svcn = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ins_ext: ++ if (evcn1 > next_svcn) { ++ err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, ++ next_svcn, evcn1 - next_svcn, ++ attr_b->flags, &attr, &mi); ++ if (err) ++ goto out; ++ } ++ok: ++ run_truncate_around(run, vcn); ++out: ++ up_write(&ni->file.run_lock); ++ ni_unlock(ni); ++ ++ return err; ++} ++ ++int attr_data_read_resident(struct ntfs_inode *ni, struct page *page) ++{ ++ u64 vbo; ++ struct ATTRIB *attr; ++ u32 data_size; ++ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL); ++ if (!attr) ++ return -EINVAL; ++ ++ if (attr->non_res) ++ return E_NTFS_NONRESIDENT; ++ ++ vbo = page->index << PAGE_SHIFT; ++ data_size = le32_to_cpu(attr->res.data_size); ++ if (vbo < data_size) { ++ const char *data = resident_data(attr); ++ char *kaddr = kmap_atomic(page); ++ u32 use = data_size - vbo; ++ ++ if (use > PAGE_SIZE) ++ use = PAGE_SIZE; ++ ++ memcpy(kaddr, data + vbo, use); ++ memset(kaddr + use, 0, PAGE_SIZE - use); ++ kunmap_atomic(kaddr); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ } else if (!PageUptodate(page)) { ++ zero_user_segment(page, 0, PAGE_SIZE); ++ SetPageUptodate(page); ++ } ++ ++ return 0; ++} ++ ++int attr_data_write_resident(struct ntfs_inode *ni, struct page *page) ++{ ++ u64 vbo; ++ struct mft_inode *mi; ++ struct ATTRIB *attr; ++ u32 data_size; ++ ++ attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi); ++ if (!attr) ++ return -EINVAL; ++ ++ if (attr->non_res) { ++ /*return special error code to check this case*/ ++ return E_NTFS_NONRESIDENT; ++ } ++ ++ vbo = page->index << PAGE_SHIFT; ++ data_size = le32_to_cpu(attr->res.data_size); ++ if (vbo < data_size) { ++ char *data = resident_data(attr); ++ char *kaddr = kmap_atomic(page); ++ u32 use = data_size - vbo; ++ ++ if (use > PAGE_SIZE) ++ use = PAGE_SIZE; ++ memcpy(data + vbo, kaddr, use); ++ kunmap_atomic(kaddr); ++ mi->dirty = true; ++ } ++ ni->i_valid = data_size; ++ ++ return 0; ++} ++ ++/* ++ * attr_load_runs_vcn ++ * ++ * load runs with vcn ++ */ ++int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, struct runs_tree *run, ++ CLST vcn) ++{ ++ struct ATTRIB *attr; ++ int err; ++ CLST svcn, evcn; ++ u16 ro; ++ ++ attr = ni_find_attr(ni, NULL, NULL, type, name, name_len, &vcn, NULL); ++ if (!attr) ++ return -ENOENT; ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ ++ if (evcn < vcn || vcn < svcn) ++ return -EINVAL; ++ ++ ro = le16_to_cpu(attr->nres.run_off); ++ err = run_unpack_ex(run, ni->mi.sbi, ni->mi.rno, svcn, evcn, svcn, ++ Add2Ptr(attr, ro), le32_to_cpu(attr->size) - ro); ++ if (err < 0) ++ return err; ++ return 0; ++} ++ ++/* ++ * load runs for given range [from to) ++ */ ++int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, struct runs_tree *run, ++ u64 from, u64 to) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ u8 cluster_bits = sbi->cluster_bits; ++ CLST vcn = from >> cluster_bits; ++ CLST vcn_last = (to - 1) >> cluster_bits; ++ CLST lcn, clen; ++ int err; ++ ++ for (vcn = from >> cluster_bits; vcn <= vcn_last; vcn += clen) { ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, NULL)) { ++ err = attr_load_runs_vcn(ni, type, name, name_len, run, ++ vcn); ++ if (err) ++ return err; ++ clen = 0; /*next run_lookup_entry(vcn) must be success*/ ++ } ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++/* ++ * attr_wof_frame_info ++ * ++ * read header of xpress/lzx file to get info about frame ++ */ ++int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct runs_tree *run, u64 frame, u64 frames, ++ u8 frame_bits, u32 *ondisk_size, u64 *vbo_data) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ u64 vbo[2], off[2], wof_size; ++ u32 voff; ++ u8 bytes_per_off; ++ char *addr; ++ struct page *page; ++ int i, err; ++ __le32 *off32; ++ __le64 *off64; ++ ++ if (ni->vfs_inode.i_size < 0x100000000ull) { ++ /* file starts with array of 32 bit offsets */ ++ bytes_per_off = sizeof(__le32); ++ vbo[1] = frame << 2; ++ *vbo_data = frames << 2; ++ } else { ++ /* file starts with array of 64 bit offsets */ ++ bytes_per_off = sizeof(__le64); ++ vbo[1] = frame << 3; ++ *vbo_data = frames << 3; ++ } ++ ++ /* ++ * read 4/8 bytes at [vbo - 4(8)] == offset where compressed frame starts ++ * read 4/8 bytes at [vbo] == offset where compressed frame ends ++ */ ++ if (!attr->non_res) { ++ if (vbo[1] + bytes_per_off > le32_to_cpu(attr->res.data_size)) { ++ ntfs_inode_err(&ni->vfs_inode, "is corrupted"); ++ return -EINVAL; ++ } ++ addr = resident_data(attr); ++ ++ if (bytes_per_off == sizeof(__le32)) { ++ off32 = Add2Ptr(addr, vbo[1]); ++ off[0] = vbo[1] ? le32_to_cpu(off32[-1]) : 0; ++ off[1] = le32_to_cpu(off32[0]); ++ } else { ++ off64 = Add2Ptr(addr, vbo[1]); ++ off[0] = vbo[1] ? le64_to_cpu(off64[-1]) : 0; ++ off[1] = le64_to_cpu(off64[0]); ++ } ++ ++ *vbo_data += off[0]; ++ *ondisk_size = off[1] - off[0]; ++ return 0; ++ } ++ ++ wof_size = le64_to_cpu(attr->nres.data_size); ++ down_write(&ni->file.run_lock); ++ page = ni->file.offs_page; ++ if (!page) { ++ page = alloc_page(GFP_KERNEL); ++ if (!page) { ++ err = -ENOMEM; ++ goto out; ++ } ++ page->index = -1; ++ ni->file.offs_page = page; ++ } ++ lock_page(page); ++ addr = page_address(page); ++ ++ if (vbo[1]) { ++ voff = vbo[1] & (PAGE_SIZE - 1); ++ vbo[0] = vbo[1] - bytes_per_off; ++ i = 0; ++ } else { ++ voff = 0; ++ vbo[0] = 0; ++ off[0] = 0; ++ i = 1; ++ } ++ ++ do { ++ pgoff_t index = vbo[i] >> PAGE_SHIFT; ++ ++ if (index != page->index) { ++ u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1); ++ u64 to = min(from + PAGE_SIZE, wof_size); ++ ++ err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME, ++ ARRAY_SIZE(WOF_NAME), run, ++ from, to); ++ if (err) ++ goto out1; ++ ++ err = ntfs_bio_pages(sbi, run, &page, 1, from, ++ to - from, REQ_OP_READ); ++ if (err) { ++ page->index = -1; ++ goto out1; ++ } ++ page->index = index; ++ } ++ ++ if (i) { ++ if (bytes_per_off == sizeof(__le32)) { ++ off32 = Add2Ptr(addr, voff); ++ off[1] = le32_to_cpu(*off32); ++ } else { ++ off64 = Add2Ptr(addr, voff); ++ off[1] = le64_to_cpu(*off64); ++ } ++ } else if (!voff) { ++ if (bytes_per_off == sizeof(__le32)) { ++ off32 = Add2Ptr(addr, PAGE_SIZE - sizeof(u32)); ++ off[0] = le32_to_cpu(*off32); ++ } else { ++ off64 = Add2Ptr(addr, PAGE_SIZE - sizeof(u64)); ++ off[0] = le64_to_cpu(*off64); ++ } ++ } else { ++ /* two values in one page*/ ++ if (bytes_per_off == sizeof(__le32)) { ++ off32 = Add2Ptr(addr, voff); ++ off[0] = le32_to_cpu(off32[-1]); ++ off[1] = le32_to_cpu(off32[0]); ++ } else { ++ off64 = Add2Ptr(addr, voff); ++ off[0] = le64_to_cpu(off64[-1]); ++ off[1] = le64_to_cpu(off64[0]); ++ } ++ break; ++ } ++ } while (++i < 2); ++ ++ *vbo_data += off[0]; ++ *ondisk_size = off[1] - off[0]; ++ ++out1: ++ unlock_page(page); ++out: ++ up_write(&ni->file.run_lock); ++ return err; ++} ++#endif ++ ++/* ++ * attr_is_frame_compressed ++ * ++ * This function is used to detect compressed frame ++ */ ++int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, ++ CLST frame, CLST *clst_data) ++{ ++ int err; ++ u32 clst_frame; ++ CLST clen, lcn, vcn, alen, slen, vcn_next; ++ size_t idx; ++ struct runs_tree *run; ++ ++ *clst_data = 0; ++ ++ if (!is_attr_compressed(attr)) ++ return 0; ++ ++ if (!attr->non_res) ++ return 0; ++ ++ clst_frame = 1u << attr->nres.c_unit; ++ vcn = frame * clst_frame; ++ run = &ni->file.run; ++ ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { ++ err = attr_load_runs_vcn(ni, attr->type, attr_name(attr), ++ attr->name_len, run, vcn); ++ if (err) ++ return err; ++ ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) ++ return -EINVAL; ++ } ++ ++ if (lcn == SPARSE_LCN) { ++ /* sparsed frame */ ++ return 0; ++ } ++ ++ if (clen >= clst_frame) { ++ /* ++ * The frame is not compressed 'cause ++ * it does not contain any sparse clusters ++ */ ++ *clst_data = clst_frame; ++ return 0; ++ } ++ ++ alen = bytes_to_cluster(ni->mi.sbi, le64_to_cpu(attr->nres.alloc_size)); ++ slen = 0; ++ *clst_data = clen; ++ ++ /* ++ * The frame is compressed if *clst_data + slen >= clst_frame ++ * Check next fragments ++ */ ++ while ((vcn += clen) < alen) { ++ vcn_next = vcn; ++ ++ if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || ++ vcn_next != vcn) { ++ err = attr_load_runs_vcn(ni, attr->type, ++ attr_name(attr), ++ attr->name_len, run, vcn_next); ++ if (err) ++ return err; ++ vcn = vcn_next; ++ ++ if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) ++ return -EINVAL; ++ } ++ ++ if (lcn == SPARSE_LCN) { ++ slen += clen; ++ } else { ++ if (slen) { ++ /* ++ * data_clusters + sparse_clusters = ++ * not enough for frame ++ */ ++ return -EINVAL; ++ } ++ *clst_data += clen; ++ } ++ ++ if (*clst_data + slen >= clst_frame) { ++ if (!slen) { ++ /* ++ * There is no sparsed clusters in this frame ++ * So it is not compressed ++ */ ++ *clst_data = clst_frame; ++ } else { ++ /*frame is compressed*/ ++ } ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * attr_allocate_frame ++ * ++ * allocate/free clusters for 'frame' ++ * assumed: down_write(&ni->file.run_lock); ++ */ ++int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, ++ u64 new_valid) ++{ ++ int err = 0; ++ struct runs_tree *run = &ni->file.run; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTRIB *attr = NULL, *attr_b; ++ struct ATTR_LIST_ENTRY *le, *le_b; ++ struct mft_inode *mi, *mi_b; ++ CLST svcn, evcn1, next_svcn, lcn, len; ++ CLST vcn, end, clst_data; ++ u64 total_size, valid_size, data_size; ++ ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); ++ if (!attr_b) ++ return -ENOENT; ++ ++ if (!is_attr_ext(attr_b)) ++ return -EINVAL; ++ ++ vcn = frame << NTFS_LZNT_CUNIT; ++ total_size = le64_to_cpu(attr_b->nres.total_size); ++ ++ svcn = le64_to_cpu(attr_b->nres.svcn); ++ evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1; ++ data_size = le64_to_cpu(attr_b->nres.data_size); ++ ++ if (svcn <= vcn && vcn < evcn1) { ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ } else if (!le_b) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ le = le_b; ++ attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, ++ &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn1 = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ ++ err = attr_load_runs(attr, ni, run, NULL); ++ if (err) ++ goto out; ++ ++ err = attr_is_frame_compressed(ni, attr_b, frame, &clst_data); ++ if (err) ++ goto out; ++ ++ total_size -= (u64)clst_data << sbi->cluster_bits; ++ ++ len = bytes_to_cluster(sbi, compr_size); ++ ++ if (len == clst_data) ++ goto out; ++ ++ if (len < clst_data) { ++ err = run_deallocate_ex(sbi, run, vcn + len, clst_data - len, ++ NULL, true); ++ if (err) ++ goto out; ++ ++ if (!run_add_entry(run, vcn + len, SPARSE_LCN, clst_data - len, ++ false)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ end = vcn + clst_data; ++ /* run contains updated range [vcn + len : end) */ ++ } else { ++ CLST alen, hint = 0; ++ /* Get the last lcn to allocate from */ ++ if (vcn + clst_data && ++ !run_lookup_entry(run, vcn + clst_data - 1, &hint, NULL, ++ NULL)) { ++ hint = -1; ++ } ++ ++ err = attr_allocate_clusters(sbi, run, vcn + clst_data, ++ hint + 1, len - clst_data, NULL, 0, ++ &alen, 0, &lcn); ++ if (err) ++ goto out; ++ ++ end = vcn + len; ++ /* run contains updated range [vcn + clst_data : end) */ ++ } ++ ++ total_size += (u64)len << sbi->cluster_bits; ++ ++repack: ++ err = mi_pack_runs(mi, attr, run, max(end, evcn1) - svcn); ++ if (err) ++ goto out; ++ ++ attr_b->nres.total_size = cpu_to_le64(total_size); ++ inode_set_bytes(&ni->vfs_inode, total_size); ++ ++ mi_b->dirty = true; ++ mark_inode_dirty(&ni->vfs_inode); ++ ++ /* stored [vcn : next_svcn) from [vcn : end) */ ++ next_svcn = le64_to_cpu(attr->nres.evcn) + 1; ++ ++ if (end <= evcn1) { ++ if (next_svcn == evcn1) { ++ /* Normal way. update attribute and exit */ ++ goto ok; ++ } ++ /* add new segment [next_svcn : evcn1 - next_svcn )*/ ++ if (!ni->attr_list.size) { ++ err = ni_create_attr_list(ni); ++ if (err) ++ goto out; ++ /* layout of records is changed */ ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, ++ 0, NULL, &mi_b); ++ if (!attr_b) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ goto repack; ++ } ++ } ++ ++ svcn = evcn1; ++ ++ /* Estimate next attribute */ ++ attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); ++ ++ if (attr) { ++ CLST alloc = bytes_to_cluster( ++ sbi, le64_to_cpu(attr_b->nres.alloc_size)); ++ CLST evcn = le64_to_cpu(attr->nres.evcn); ++ ++ if (end < next_svcn) ++ end = next_svcn; ++ while (end > evcn) { ++ /* remove segment [svcn : evcn)*/ ++ mi_remove_attr(mi, attr); ++ ++ if (!al_remove_le(ni, le)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (evcn + 1 >= alloc) { ++ /* last attribute segment */ ++ evcn1 = evcn + 1; ++ goto ins_ext; ++ } ++ ++ if (ni_load_mi(ni, le, &mi)) { ++ attr = NULL; ++ goto out; ++ } ++ ++ attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, 0, ++ &le->id); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ } ++ ++ if (end < svcn) ++ end = svcn; ++ ++ err = attr_load_runs(attr, ni, run, &end); ++ if (err) ++ goto out; ++ ++ evcn1 = evcn + 1; ++ attr->nres.svcn = cpu_to_le64(next_svcn); ++ err = mi_pack_runs(mi, attr, run, evcn1 - next_svcn); ++ if (err) ++ goto out; ++ ++ le->vcn = cpu_to_le64(next_svcn); ++ ni->attr_list.dirty = true; ++ mi->dirty = true; ++ ++ next_svcn = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ins_ext: ++ if (evcn1 > next_svcn) { ++ err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, ++ next_svcn, evcn1 - next_svcn, ++ attr_b->flags, &attr, &mi); ++ if (err) ++ goto out; ++ } ++ok: ++ run_truncate_around(run, vcn); ++out: ++ if (new_valid > data_size) ++ new_valid = data_size; ++ ++ valid_size = le64_to_cpu(attr_b->nres.valid_size); ++ if (new_valid != valid_size) { ++ attr_b->nres.valid_size = cpu_to_le64(valid_size); ++ mi_b->dirty = true; ++ } ++ ++ return err; ++} ++ ++/* Collapse range in file */ ++int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) ++{ ++ int err = 0; ++ struct runs_tree *run = &ni->file.run; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTRIB *attr = NULL, *attr_b; ++ struct ATTR_LIST_ENTRY *le, *le_b; ++ struct mft_inode *mi, *mi_b; ++ CLST svcn, evcn1, len, dealloc, alen; ++ CLST vcn, end; ++ u64 valid_size, data_size, alloc_size, total_size; ++ u32 mask; ++ __le16 a_flags; ++ ++ if (!bytes) ++ return 0; ++ ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); ++ if (!attr_b) ++ return -ENOENT; ++ ++ if (!attr_b->non_res) { ++ /* Attribute is resident. Nothing to do? */ ++ return 0; ++ } ++ ++ data_size = le64_to_cpu(attr_b->nres.data_size); ++ alloc_size = le64_to_cpu(attr_b->nres.alloc_size); ++ a_flags = attr_b->flags; ++ ++ if (is_attr_ext(attr_b)) { ++ total_size = le64_to_cpu(attr_b->nres.total_size); ++ mask = (sbi->cluster_size << attr_b->nres.c_unit) - 1; ++ } else { ++ total_size = alloc_size; ++ mask = sbi->cluster_mask; ++ } ++ ++ if ((vbo & mask) || (bytes & mask)) { ++ /* allow to collapse only cluster aligned ranges */ ++ return -EINVAL; ++ } ++ ++ if (vbo > data_size) ++ return -EINVAL; ++ ++ down_write(&ni->file.run_lock); ++ ++ if (vbo + bytes >= data_size) { ++ u64 new_valid = min(ni->i_valid, vbo); ++ ++ /* Simple truncate file at 'vbo' */ ++ truncate_setsize(&ni->vfs_inode, vbo); ++ err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, vbo, ++ &new_valid, true, NULL); ++ ++ if (!err && new_valid < ni->i_valid) ++ ni->i_valid = new_valid; ++ ++ goto out; ++ } ++ ++ /* ++ * Enumerate all attribute segments and collapse ++ */ ++ alen = alloc_size >> sbi->cluster_bits; ++ vcn = vbo >> sbi->cluster_bits; ++ len = bytes >> sbi->cluster_bits; ++ end = vcn + len; ++ dealloc = 0; ++ ++ svcn = le64_to_cpu(attr_b->nres.svcn); ++ evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1; ++ ++ if (svcn <= vcn && vcn < evcn1) { ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ } else if (!le_b) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ le = le_b; ++ attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, ++ &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn1 = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ ++ for (;;) { ++ if (svcn >= end) { ++ /* shift vcn */ ++ attr->nres.svcn = cpu_to_le64(svcn - len); ++ attr->nres.evcn = cpu_to_le64(evcn1 - 1 - len); ++ if (le) { ++ le->vcn = attr->nres.svcn; ++ ni->attr_list.dirty = true; ++ } ++ mi->dirty = true; ++ } else if (svcn < vcn || end < evcn1) { ++ CLST vcn1, eat, next_svcn; ++ ++ /* collapse a part of this attribute segment */ ++ err = attr_load_runs(attr, ni, run, &svcn); ++ if (err) ++ goto out; ++ vcn1 = max(vcn, svcn); ++ eat = min(end, evcn1) - vcn1; ++ ++ err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc, ++ true); ++ if (err) ++ goto out; ++ ++ if (!run_collapse_range(run, vcn1, eat)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (svcn >= vcn) { ++ /* shift vcn */ ++ attr->nres.svcn = cpu_to_le64(vcn); ++ if (le) { ++ le->vcn = attr->nres.svcn; ++ ni->attr_list.dirty = true; ++ } ++ } ++ ++ err = mi_pack_runs(mi, attr, run, evcn1 - svcn - eat); ++ if (err) ++ goto out; ++ ++ next_svcn = le64_to_cpu(attr->nres.evcn) + 1; ++ if (next_svcn + eat < evcn1) { ++ err = ni_insert_nonresident( ++ ni, ATTR_DATA, NULL, 0, run, next_svcn, ++ evcn1 - eat - next_svcn, a_flags, &attr, ++ &mi); ++ if (err) ++ goto out; ++ ++ /* layout of records maybe changed */ ++ attr_b = NULL; ++ le = al_find_ex(ni, NULL, ATTR_DATA, NULL, 0, ++ &next_svcn); ++ if (!le) { ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ /* free all allocated memory */ ++ run_truncate(run, 0); ++ } else { ++ u16 le_sz; ++ u16 roff = le16_to_cpu(attr->nres.run_off); ++ ++ /*run==1 means unpack and deallocate*/ ++ run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, ++ evcn1 - 1, svcn, Add2Ptr(attr, roff), ++ le32_to_cpu(attr->size) - roff); ++ ++ /* delete this attribute segment */ ++ mi_remove_attr(mi, attr); ++ if (!le) ++ break; ++ ++ le_sz = le16_to_cpu(le->size); ++ if (!al_remove_le(ni, le)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (evcn1 >= alen) ++ break; ++ ++ if (!svcn) { ++ /* Load next record that contains this attribute */ ++ if (ni_load_mi(ni, le, &mi)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Look for required attribute */ ++ attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, ++ 0, &le->id); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ goto next_attr; ++ } ++ le = (struct ATTR_LIST_ENTRY *)((u8 *)le - le_sz); ++ } ++ ++ if (evcn1 >= alen) ++ break; ++ ++ attr = ni_enum_attr_ex(ni, attr, &le, &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++next_attr: ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn1 = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ ++ if (!attr_b) { ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, ++ &mi_b); ++ if (!attr_b) { ++ err = -ENOENT; ++ goto out; ++ } ++ } ++ ++ data_size -= bytes; ++ valid_size = ni->i_valid; ++ if (vbo + bytes <= valid_size) ++ valid_size -= bytes; ++ else if (vbo < valid_size) ++ valid_size = vbo; ++ ++ attr_b->nres.alloc_size = cpu_to_le64(alloc_size - bytes); ++ attr_b->nres.data_size = cpu_to_le64(data_size); ++ attr_b->nres.valid_size = cpu_to_le64(min(valid_size, data_size)); ++ total_size -= (u64)dealloc << sbi->cluster_bits; ++ if (is_attr_ext(attr_b)) ++ attr_b->nres.total_size = cpu_to_le64(total_size); ++ mi_b->dirty = true; ++ ++ /*update inode size*/ ++ ni->i_valid = valid_size; ++ ni->vfs_inode.i_size = data_size; ++ inode_set_bytes(&ni->vfs_inode, total_size); ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ mark_inode_dirty(&ni->vfs_inode); ++ ++out: ++ up_write(&ni->file.run_lock); ++ if (err) ++ make_bad_inode(&ni->vfs_inode); ++ ++ return err; ++} ++ ++/* not for normal files */ ++int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) ++{ ++ int err = 0; ++ struct runs_tree *run = &ni->file.run; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTRIB *attr = NULL, *attr_b; ++ struct ATTR_LIST_ENTRY *le, *le_b; ++ struct mft_inode *mi, *mi_b; ++ CLST svcn, evcn1, vcn, len, end, alen, dealloc; ++ u64 total_size, alloc_size; ++ u32 mask; ++ ++ if (!bytes) ++ return 0; ++ ++ le_b = NULL; ++ attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b); ++ if (!attr_b) ++ return -ENOENT; ++ ++ if (!attr_b->non_res) { ++ u32 data_size = le32_to_cpu(attr->res.data_size); ++ u32 from, to; ++ ++ if (vbo > data_size) ++ return 0; ++ ++ from = vbo; ++ to = (vbo + bytes) < data_size ? (vbo + bytes) : data_size; ++ memset(Add2Ptr(resident_data(attr_b), from), 0, to - from); ++ return 0; ++ } ++ ++ if (!is_attr_ext(attr_b)) ++ return -EOPNOTSUPP; ++ ++ alloc_size = le64_to_cpu(attr_b->nres.alloc_size); ++ total_size = le64_to_cpu(attr_b->nres.total_size); ++ ++ if (vbo >= alloc_size) { ++ // NOTE: it is allowed ++ return 0; ++ } ++ ++ mask = (sbi->cluster_size << attr_b->nres.c_unit) - 1; ++ ++ bytes += vbo; ++ if (bytes > alloc_size) ++ bytes = alloc_size; ++ bytes -= vbo; ++ ++ if ((vbo & mask) || (bytes & mask)) { ++ /* We have to zero a range(s)*/ ++ if (frame_size == NULL) { ++ /* Caller insists range is aligned */ ++ return -EINVAL; ++ } ++ *frame_size = mask + 1; ++ return E_NTFS_NOTALIGNED; ++ } ++ ++ down_write(&ni->file.run_lock); ++ /* ++ * Enumerate all attribute segments and punch hole where necessary ++ */ ++ alen = alloc_size >> sbi->cluster_bits; ++ vcn = vbo >> sbi->cluster_bits; ++ len = bytes >> sbi->cluster_bits; ++ end = vcn + len; ++ dealloc = 0; ++ ++ svcn = le64_to_cpu(attr_b->nres.svcn); ++ evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1; ++ ++ if (svcn <= vcn && vcn < evcn1) { ++ attr = attr_b; ++ le = le_b; ++ mi = mi_b; ++ } else if (!le_b) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ le = le_b; ++ attr = ni_find_attr(ni, attr_b, &le, ATTR_DATA, NULL, 0, &vcn, ++ &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn1 = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ ++ while (svcn < end) { ++ CLST vcn1, zero, dealloc2; ++ ++ err = attr_load_runs(attr, ni, run, &svcn); ++ if (err) ++ goto out; ++ vcn1 = max(vcn, svcn); ++ zero = min(end, evcn1) - vcn1; ++ ++ dealloc2 = dealloc; ++ err = run_deallocate_ex(sbi, run, vcn1, zero, &dealloc, true); ++ if (err) ++ goto out; ++ ++ if (dealloc2 == dealloc) { ++ /* looks like the required range is already sparsed */ ++ } else { ++ if (!run_add_entry(run, vcn1, SPARSE_LCN, zero, ++ false)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = mi_pack_runs(mi, attr, run, evcn1 - svcn); ++ if (err) ++ goto out; ++ } ++ /* free all allocated memory */ ++ run_truncate(run, 0); ++ ++ if (evcn1 >= alen) ++ break; ++ ++ attr = ni_enum_attr_ex(ni, attr, &le, &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn1 = le64_to_cpu(attr->nres.evcn) + 1; ++ } ++ ++ total_size -= (u64)dealloc << sbi->cluster_bits; ++ attr_b->nres.total_size = cpu_to_le64(total_size); ++ mi_b->dirty = true; ++ ++ /*update inode size*/ ++ inode_set_bytes(&ni->vfs_inode, total_size); ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ mark_inode_dirty(&ni->vfs_inode); ++ ++out: ++ up_write(&ni->file.run_lock); ++ if (err) ++ make_bad_inode(&ni->vfs_inode); ++ ++ return err; ++} +diff --git a/src/attrlist.c b/src/attrlist.c +new file mode 100644 +index 0000000000000000000000000000000000000000..ea561361b576d5a8a1ec30c0017a5ac76f1a61df +--- /dev/null ++++ b/src/attrlist.c +@@ -0,0 +1,456 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* Returns true if le is valid */ ++static inline bool al_is_valid_le(const struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le) ++{ ++ if (!le || !ni->attr_list.le || !ni->attr_list.size) ++ return false; ++ ++ return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <= ++ ni->attr_list.size; ++} ++ ++void al_destroy(struct ntfs_inode *ni) ++{ ++ run_close(&ni->attr_list.run); ++ ntfs_free(ni->attr_list.le); ++ ni->attr_list.le = NULL; ++ ni->attr_list.size = 0; ++ ni->attr_list.dirty = false; ++} ++ ++/* ++ * ntfs_load_attr_list ++ * ++ * This method makes sure that the ATTRIB list, if present, ++ * has been properly set up. ++ */ ++int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) ++{ ++ int err; ++ size_t lsize; ++ void *le = NULL; ++ ++ if (ni->attr_list.size) ++ return 0; ++ ++ if (!attr->non_res) { ++ lsize = le32_to_cpu(attr->res.data_size); ++ le = ntfs_malloc(al_aligned(lsize)); ++ if (!le) { ++ err = -ENOMEM; ++ goto out; ++ } ++ memcpy(le, resident_data(attr), lsize); ++ } else if (attr->nres.svcn) { ++ err = -EINVAL; ++ goto out; ++ } else { ++ u16 run_off = le16_to_cpu(attr->nres.run_off); ++ ++ lsize = le64_to_cpu(attr->nres.data_size); ++ ++ run_init(&ni->attr_list.run); ++ ++ err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno, ++ 0, le64_to_cpu(attr->nres.evcn), 0, ++ Add2Ptr(attr, run_off), ++ le32_to_cpu(attr->size) - run_off); ++ if (err < 0) ++ goto out; ++ ++ le = ntfs_malloc(al_aligned(lsize)); ++ if (!le) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le, ++ lsize, NULL); ++ if (err) ++ goto out; ++ } ++ ++ ni->attr_list.size = lsize; ++ ni->attr_list.le = le; ++ ++ return 0; ++ ++out: ++ ni->attr_list.le = le; ++ al_destroy(ni); ++ ++ return err; ++} ++ ++/* ++ * al_enumerate ++ * ++ * Returns the next list 'le' ++ * if 'le' is NULL then returns the first 'le' ++ */ ++struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le) ++{ ++ size_t off; ++ u16 sz; ++ ++ if (!le) { ++ le = ni->attr_list.le; ++ } else { ++ sz = le16_to_cpu(le->size); ++ if (sz < sizeof(struct ATTR_LIST_ENTRY)) { ++ /* Impossible 'cause we should not return such 'le' */ ++ return NULL; ++ } ++ le = Add2Ptr(le, sz); ++ } ++ ++ /* Check boundary */ ++ off = PtrOffset(ni->attr_list.le, le); ++ if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) { ++ // The regular end of list ++ return NULL; ++ } ++ ++ sz = le16_to_cpu(le->size); ++ ++ /* Check 'le' for errors */ ++ if (sz < sizeof(struct ATTR_LIST_ENTRY) || ++ off + sz > ni->attr_list.size || ++ sz < le->name_off + le->name_len * sizeof(short)) { ++ return NULL; ++ } ++ ++ return le; ++} ++ ++/* ++ * al_find_le ++ * ++ * finds the first 'le' in the list which matches type, name and vcn ++ * Returns NULL if not found ++ */ ++struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le, ++ const struct ATTRIB *attr) ++{ ++ CLST svcn = attr_svcn(attr); ++ ++ return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len, ++ &svcn); ++} ++ ++/* ++ * al_find_ex ++ * ++ * finds the first 'le' in the list which matches type, name and vcn ++ * Returns NULL if not found ++ */ ++struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, ++ struct ATTR_LIST_ENTRY *le, ++ enum ATTR_TYPE type, const __le16 *name, ++ u8 name_len, const CLST *vcn) ++{ ++ struct ATTR_LIST_ENTRY *ret = NULL; ++ u32 type_in = le32_to_cpu(type); ++ ++ while ((le = al_enumerate(ni, le))) { ++ u64 le_vcn; ++ int diff = le32_to_cpu(le->type) - type_in; ++ ++ /* List entries are sorted by type, name and vcn */ ++ if (diff < 0) ++ continue; ++ ++ if (diff > 0) ++ return ret; ++ ++ if (le->name_len != name_len) ++ continue; ++ ++ le_vcn = le64_to_cpu(le->vcn); ++ if (!le_vcn) { ++ /* ++ * compare entry names only for entry with vcn == 0 ++ */ ++ diff = ntfs_cmp_names(le_name(le), name_len, name, ++ name_len, ni->mi.sbi->upcase, ++ true); ++ if (diff < 0) ++ continue; ++ ++ if (diff > 0) ++ return ret; ++ } ++ ++ if (!vcn) ++ return le; ++ ++ if (*vcn == le_vcn) ++ return le; ++ ++ if (*vcn < le_vcn) ++ return ret; ++ ++ ret = le; ++ } ++ ++ return ret; ++} ++ ++/* ++ * al_find_le_to_insert ++ * ++ * finds the first list entry which matches type, name and vcn ++ */ ++static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, ++ enum ATTR_TYPE type, ++ const __le16 *name, ++ u8 name_len, CLST vcn) ++{ ++ struct ATTR_LIST_ENTRY *le = NULL, *prev; ++ u32 type_in = le32_to_cpu(type); ++ ++ /* List entries are sorted by type, name, vcn */ ++ while ((le = al_enumerate(ni, prev = le))) { ++ int diff = le32_to_cpu(le->type) - type_in; ++ ++ if (diff < 0) ++ continue; ++ ++ if (diff > 0) ++ return le; ++ ++ if (!le->vcn) { ++ /* ++ * compare entry names only for entry with vcn == 0 ++ */ ++ diff = ntfs_cmp_names(le_name(le), le->name_len, name, ++ name_len, ni->mi.sbi->upcase, ++ true); ++ if (diff < 0) ++ continue; ++ ++ if (diff > 0) ++ return le; ++ } ++ ++ if (le64_to_cpu(le->vcn) >= vcn) ++ return le; ++ } ++ ++ return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le; ++} ++ ++/* ++ * al_add_le ++ * ++ * adds an "attribute list entry" to the list. ++ */ ++int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, ++ u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref, ++ struct ATTR_LIST_ENTRY **new_le) ++{ ++ int err; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ size_t off; ++ u16 sz; ++ size_t asize, new_asize; ++ u64 new_size; ++ typeof(ni->attr_list) *al = &ni->attr_list; ++ ++ /* ++ * Compute the size of the new 'le' ++ */ ++ sz = le_size(name_len); ++ new_size = al->size + sz; ++ asize = al_aligned(al->size); ++ new_asize = al_aligned(new_size); ++ ++ /* Scan forward to the point at which the new 'le' should be inserted. */ ++ le = al_find_le_to_insert(ni, type, name, name_len, svcn); ++ off = PtrOffset(al->le, le); ++ ++ if (new_size > asize) { ++ void *ptr = ntfs_malloc(new_asize); ++ ++ if (!ptr) ++ return -ENOMEM; ++ ++ memcpy(ptr, al->le, off); ++ memcpy(Add2Ptr(ptr, off + sz), le, al->size - off); ++ le = Add2Ptr(ptr, off); ++ ntfs_free(al->le); ++ al->le = ptr; ++ } else { ++ memmove(Add2Ptr(le, sz), le, al->size - off); ++ } ++ ++ al->size = new_size; ++ ++ le->type = type; ++ le->size = cpu_to_le16(sz); ++ le->name_len = name_len; ++ le->name_off = offsetof(struct ATTR_LIST_ENTRY, name); ++ le->vcn = cpu_to_le64(svcn); ++ le->ref = *ref; ++ le->id = id; ++ memcpy(le->name, name, sizeof(short) * name_len); ++ ++ al->dirty = true; ++ ++ err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size, ++ &new_size, true, &attr); ++ if (err) ++ return err; ++ ++ if (attr && attr->non_res) { ++ err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, ++ al->size); ++ if (err) ++ return err; ++ } ++ ++ al->dirty = false; ++ *new_le = le; ++ ++ return 0; ++} ++ ++/* ++ * al_remove_le ++ * ++ * removes 'le' from attribute list ++ */ ++bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) ++{ ++ u16 size; ++ size_t off; ++ typeof(ni->attr_list) *al = &ni->attr_list; ++ ++ if (!al_is_valid_le(ni, le)) ++ return false; ++ ++ /* Save on stack the size of 'le' */ ++ size = le16_to_cpu(le->size); ++ off = PtrOffset(al->le, le); ++ ++ memmove(le, Add2Ptr(le, size), al->size - (off + size)); ++ ++ al->size -= size; ++ al->dirty = true; ++ ++ return true; ++} ++ ++/* ++ * al_delete_le ++ * ++ * deletes from the list the first 'le' which matches its parameters. ++ */ ++bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, ++ const __le16 *name, size_t name_len, ++ const struct MFT_REF *ref) ++{ ++ u16 size; ++ struct ATTR_LIST_ENTRY *le; ++ size_t off; ++ typeof(ni->attr_list) *al = &ni->attr_list; ++ ++ /* Scan forward to the first 'le' that matches the input */ ++ le = al_find_ex(ni, NULL, type, name, name_len, &vcn); ++ if (!le) ++ return false; ++ ++ off = PtrOffset(al->le, le); ++ ++next: ++ if (off >= al->size) ++ return false; ++ if (le->type != type) ++ return false; ++ if (le->name_len != name_len) ++ return false; ++ if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len, ++ ni->mi.sbi->upcase, true)) ++ return false; ++ if (le64_to_cpu(le->vcn) != vcn) ++ return false; ++ ++ /* ++ * The caller specified a segment reference, so we have to ++ * scan through the matching entries until we find that segment ++ * reference or we run of matching entries. ++ */ ++ if (ref && memcmp(ref, &le->ref, sizeof(*ref))) { ++ off += le16_to_cpu(le->size); ++ le = Add2Ptr(al->le, off); ++ goto next; ++ } ++ ++ /* Save on stack the size of 'le' */ ++ size = le16_to_cpu(le->size); ++ /* Delete 'le'. */ ++ memmove(le, Add2Ptr(le, size), al->size - (off + size)); ++ ++ al->size -= size; ++ al->dirty = true; ++ ++ return true; ++} ++ ++/* ++ * al_update ++ */ ++int al_update(struct ntfs_inode *ni) ++{ ++ int err; ++ struct ATTRIB *attr; ++ typeof(ni->attr_list) *al = &ni->attr_list; ++ ++ if (!al->dirty || !al->size) ++ return 0; ++ ++ /* ++ * attribute list increased on demand in al_add_le ++ * attribute list decreased here ++ */ ++ err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL, ++ false, &attr); ++ if (err) ++ goto out; ++ ++ if (!attr->non_res) { ++ memcpy(resident_data(attr), al->le, al->size); ++ } else { ++ err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, ++ al->size); ++ if (err) ++ goto out; ++ ++ attr->nres.valid_size = attr->nres.data_size; ++ } ++ ++ ni->mi.dirty = true; ++ al->dirty = false; ++ ++out: ++ return err; ++} +diff --git a/src/xattr.c b/src/xattr.c +new file mode 100644 +index 0000000000000000000000000000000000000000..98871c895e77b165bede3fbd1e50b91d5e07b106 +--- /dev/null ++++ b/src/xattr.c +@@ -0,0 +1,1128 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++// clang-format off ++#define SYSTEM_DOS_ATTRIB "system.dos_attrib" ++#define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib" ++#define SYSTEM_NTFS_SECURITY "system.ntfs_security" ++// clang-format on ++ ++static inline size_t unpacked_ea_size(const struct EA_FULL *ea) ++{ ++ return ea->size ? le32_to_cpu(ea->size) ++ : DwordAlign(struct_size( ++ ea, name, ++ 1 + ea->name_len + le16_to_cpu(ea->elength))); ++} ++ ++static inline size_t packed_ea_size(const struct EA_FULL *ea) ++{ ++ return struct_size(ea, name, ++ 1 + ea->name_len + le16_to_cpu(ea->elength)) - ++ offsetof(struct EA_FULL, flags); ++} ++ ++/* ++ * find_ea ++ * ++ * assume there is at least one xattr in the list ++ */ ++static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes, ++ const char *name, u8 name_len, u32 *off) ++{ ++ *off = 0; ++ ++ if (!ea_all || !bytes) ++ return false; ++ ++ for (;;) { ++ const struct EA_FULL *ea = Add2Ptr(ea_all, *off); ++ u32 next_off = *off + unpacked_ea_size(ea); ++ ++ if (next_off > bytes) ++ return false; ++ ++ if (ea->name_len == name_len && ++ !memcmp(ea->name, name, name_len)) ++ return true; ++ ++ *off = next_off; ++ if (next_off >= bytes) ++ return false; ++ } ++} ++ ++/* ++ * ntfs_read_ea ++ * ++ * reads all extended attributes ++ * ea - new allocated memory ++ * info - pointer into resident data ++ */ ++static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, ++ size_t add_bytes, const struct EA_INFO **info) ++{ ++ int err; ++ struct ATTR_LIST_ENTRY *le = NULL; ++ struct ATTRIB *attr_info, *attr_ea; ++ void *ea_p; ++ u32 size; ++ ++ static_assert(le32_to_cpu(ATTR_EA_INFO) < le32_to_cpu(ATTR_EA)); ++ ++ *ea = NULL; ++ *info = NULL; ++ ++ attr_info = ++ ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL); ++ attr_ea = ++ ni_find_attr(ni, attr_info, &le, ATTR_EA, NULL, 0, NULL, NULL); ++ ++ if (!attr_ea || !attr_info) ++ return 0; ++ ++ *info = resident_data_ex(attr_info, sizeof(struct EA_INFO)); ++ if (!*info) ++ return -EINVAL; ++ ++ /* Check Ea limit */ ++ size = le32_to_cpu((*info)->size); ++ if (size > ni->mi.sbi->ea_max_size) ++ return -EFBIG; ++ ++ if (attr_size(attr_ea) > ni->mi.sbi->ea_max_size) ++ return -EFBIG; ++ ++ /* Allocate memory for packed Ea */ ++ ea_p = ntfs_malloc(size + add_bytes); ++ if (!ea_p) ++ return -ENOMEM; ++ ++ if (attr_ea->non_res) { ++ struct runs_tree run; ++ ++ run_init(&run); ++ ++ err = attr_load_runs(attr_ea, ni, &run, NULL); ++ if (!err) ++ err = ntfs_read_run_nb(ni->mi.sbi, &run, 0, ea_p, size, ++ NULL); ++ run_close(&run); ++ ++ if (err) ++ goto out; ++ } else { ++ void *p = resident_data_ex(attr_ea, size); ++ ++ if (!p) { ++ err = -EINVAL; ++ goto out; ++ } ++ memcpy(ea_p, p, size); ++ } ++ ++ memset(Add2Ptr(ea_p, size), 0, add_bytes); ++ *ea = ea_p; ++ return 0; ++ ++out: ++ ntfs_free(ea_p); ++ *ea = NULL; ++ return err; ++} ++ ++/* ++ * ntfs_list_ea ++ * ++ * copy a list of xattrs names into the buffer ++ * provided, or compute the buffer size required ++ * ++ * Returns a negative error number on failure, or the number of bytes ++ * used / required on success. ++ */ ++static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, ++ size_t bytes_per_buffer) ++{ ++ const struct EA_INFO *info; ++ struct EA_FULL *ea_all = NULL; ++ const struct EA_FULL *ea; ++ u32 off, size; ++ int err; ++ size_t ret; ++ ++ err = ntfs_read_ea(ni, &ea_all, 0, &info); ++ if (err) ++ return err; ++ ++ if (!info || !ea_all) ++ return 0; ++ ++ size = le32_to_cpu(info->size); ++ ++ /* Enumerate all xattrs */ ++ for (ret = 0, off = 0; off < size; off += unpacked_ea_size(ea)) { ++ ea = Add2Ptr(ea_all, off); ++ ++ if (buffer) { ++ if (ret + ea->name_len + 1 > bytes_per_buffer) { ++ err = -ERANGE; ++ goto out; ++ } ++ ++ memcpy(buffer + ret, ea->name, ea->name_len); ++ buffer[ret + ea->name_len] = 0; ++ } ++ ++ ret += ea->name_len + 1; ++ } ++ ++out: ++ ntfs_free(ea_all); ++ return err ? err : ret; ++} ++ ++static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, ++ void *buffer, size_t size, size_t *required) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ const struct EA_INFO *info; ++ struct EA_FULL *ea_all = NULL; ++ const struct EA_FULL *ea; ++ u32 off, len; ++ int err; ++ ++ if (!(ni->ni_flags & NI_FLAG_EA)) ++ return -ENODATA; ++ ++ if (!required) ++ ni_lock(ni); ++ ++ len = 0; ++ ++ if (name_len > 255) { ++ err = -ENAMETOOLONG; ++ goto out; ++ } ++ ++ err = ntfs_read_ea(ni, &ea_all, 0, &info); ++ if (err) ++ goto out; ++ ++ if (!info) ++ goto out; ++ ++ /* Enumerate all xattrs */ ++ if (!find_ea(ea_all, le32_to_cpu(info->size), name, name_len, &off)) { ++ err = -ENODATA; ++ goto out; ++ } ++ ea = Add2Ptr(ea_all, off); ++ ++ len = le16_to_cpu(ea->elength); ++ if (!buffer) { ++ err = 0; ++ goto out; ++ } ++ ++ if (len > size) { ++ err = -ERANGE; ++ if (required) ++ *required = len; ++ goto out; ++ } ++ ++ memcpy(buffer, ea->name + ea->name_len + 1, len); ++ err = 0; ++ ++out: ++ ntfs_free(ea_all); ++ if (!required) ++ ni_unlock(ni); ++ ++ return err ? err : len; ++} ++ ++static noinline int ntfs_set_ea(struct inode *inode, const char *name, ++ size_t name_len, const void *value, ++ size_t val_size, int flags, int locked) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ int err; ++ struct EA_INFO ea_info; ++ const struct EA_INFO *info; ++ struct EA_FULL *new_ea; ++ struct EA_FULL *ea_all = NULL; ++ size_t add, new_pack; ++ u32 off, size; ++ __le16 size_pack; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ struct mft_inode *mi; ++ struct runs_tree ea_run; ++ u64 new_sz; ++ void *p; ++ ++ if (!locked) ++ ni_lock(ni); ++ ++ run_init(&ea_run); ++ ++ if (name_len > 255) { ++ err = -ENAMETOOLONG; ++ goto out; ++ } ++ ++ add = DwordAlign(struct_size(ea_all, name, 1 + name_len + val_size)); ++ ++ err = ntfs_read_ea(ni, &ea_all, add, &info); ++ if (err) ++ goto out; ++ ++ if (!info) { ++ memset(&ea_info, 0, sizeof(ea_info)); ++ size = 0; ++ size_pack = 0; ++ } else { ++ memcpy(&ea_info, info, sizeof(ea_info)); ++ size = le32_to_cpu(ea_info.size); ++ size_pack = ea_info.size_pack; ++ } ++ ++ if (info && find_ea(ea_all, size, name, name_len, &off)) { ++ struct EA_FULL *ea; ++ size_t ea_sz; ++ ++ if (flags & XATTR_CREATE) { ++ err = -EEXIST; ++ goto out; ++ } ++ ++ ea = Add2Ptr(ea_all, off); ++ ++ /* ++ * Check simple case when we try to insert xattr with the same value ++ * e.g. ntfs_save_wsl_perm ++ */ ++ if (val_size && le16_to_cpu(ea->elength) == val_size && ++ !memcmp(ea->name + ea->name_len + 1, value, val_size)) { ++ /* xattr already contains the required value */ ++ goto out; ++ } ++ ++ /* Remove current xattr */ ++ if (ea->flags & FILE_NEED_EA) ++ le16_add_cpu(&ea_info.count, -1); ++ ++ ea_sz = unpacked_ea_size(ea); ++ ++ le16_add_cpu(&ea_info.size_pack, 0 - packed_ea_size(ea)); ++ ++ memmove(ea, Add2Ptr(ea, ea_sz), size - off - ea_sz); ++ ++ size -= ea_sz; ++ memset(Add2Ptr(ea_all, size), 0, ea_sz); ++ ++ ea_info.size = cpu_to_le32(size); ++ ++ if ((flags & XATTR_REPLACE) && !val_size) { ++ /* remove xattr */ ++ goto update_ea; ++ } ++ } else { ++ if (flags & XATTR_REPLACE) { ++ err = -ENODATA; ++ goto out; ++ } ++ ++ if (!ea_all) { ++ ea_all = ntfs_zalloc(add); ++ if (!ea_all) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ } ++ ++ /* append new xattr */ ++ new_ea = Add2Ptr(ea_all, size); ++ new_ea->size = cpu_to_le32(add); ++ new_ea->flags = 0; ++ new_ea->name_len = name_len; ++ new_ea->elength = cpu_to_le16(val_size); ++ memcpy(new_ea->name, name, name_len); ++ new_ea->name[name_len] = 0; ++ memcpy(new_ea->name + name_len + 1, value, val_size); ++ new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea); ++ ++ /* should fit into 16 bits */ ++ if (new_pack > 0xffff) { ++ err = -EFBIG; // -EINVAL? ++ goto out; ++ } ++ ea_info.size_pack = cpu_to_le16(new_pack); ++ ++ /* new size of ATTR_EA */ ++ size += add; ++ if (size > sbi->ea_max_size) { ++ err = -EFBIG; // -EINVAL? ++ goto out; ++ } ++ ea_info.size = cpu_to_le32(size); ++ ++update_ea: ++ ++ if (!info) { ++ /* Create xattr */ ++ if (!size) { ++ err = 0; ++ goto out; ++ } ++ ++ err = ni_insert_resident(ni, sizeof(struct EA_INFO), ++ ATTR_EA_INFO, NULL, 0, NULL, NULL); ++ if (err) ++ goto out; ++ ++ err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL); ++ if (err) ++ goto out; ++ } ++ ++ new_sz = size; ++ err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz, ++ false, NULL); ++ if (err) ++ goto out; ++ ++ le = NULL; ++ attr = ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (!size) { ++ /* delete xattr, ATTR_EA_INFO */ ++ err = ni_remove_attr_le(ni, attr, le); ++ if (err) ++ goto out; ++ } else { ++ p = resident_data_ex(attr, sizeof(struct EA_INFO)); ++ if (!p) { ++ err = -EINVAL; ++ goto out; ++ } ++ memcpy(p, &ea_info, sizeof(struct EA_INFO)); ++ mi->dirty = true; ++ } ++ ++ le = NULL; ++ attr = ni_find_attr(ni, NULL, &le, ATTR_EA, NULL, 0, NULL, &mi); ++ if (!attr) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (!size) { ++ /* delete xattr, ATTR_EA */ ++ err = ni_remove_attr_le(ni, attr, le); ++ if (err) ++ goto out; ++ } else if (attr->non_res) { ++ err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size); ++ if (err) ++ goto out; ++ } else { ++ p = resident_data_ex(attr, size); ++ if (!p) { ++ err = -EINVAL; ++ goto out; ++ } ++ memcpy(p, ea_all, size); ++ mi->dirty = true; ++ } ++ ++ /* Check if we delete the last xattr */ ++ if (size) ++ ni->ni_flags |= NI_FLAG_EA; ++ else ++ ni->ni_flags &= ~NI_FLAG_EA; ++ ++ if (ea_info.size_pack != size_pack) ++ ni->ni_flags |= NI_FLAG_UPDATE_PARENT; ++ mark_inode_dirty(&ni->vfs_inode); ++ ++out: ++ if (!locked) ++ ni_unlock(ni); ++ ++ run_close(&ea_run); ++ ntfs_free(ea_all); ++ ++ return err; ++} ++ ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++static inline void ntfs_posix_acl_release(struct posix_acl *acl) ++{ ++ if (acl && refcount_dec_and_test(&acl->a_refcount)) ++ kfree(acl); ++} ++ ++static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, ++ struct inode *inode, int type, ++ int locked) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ const char *name; ++ size_t name_len; ++ struct posix_acl *acl; ++ size_t req; ++ int err; ++ void *buf; ++ ++ /* allocate PATH_MAX bytes */ ++ buf = __getname(); ++ if (!buf) ++ return ERR_PTR(-ENOMEM); ++ ++ /* Possible values of 'type' was already checked above */ ++ if (type == ACL_TYPE_ACCESS) { ++ name = XATTR_NAME_POSIX_ACL_ACCESS; ++ name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; ++ } else { ++ name = XATTR_NAME_POSIX_ACL_DEFAULT; ++ name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; ++ } ++ ++ if (!locked) ++ ni_lock(ni); ++ ++ err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX, &req); ++ ++ if (!locked) ++ ni_unlock(ni); ++ ++ /* Translate extended attribute to acl */ ++ if (err > 0) { ++ acl = posix_acl_from_xattr(mnt_userns, buf, err); ++ if (!IS_ERR(acl)) ++ set_cached_acl(inode, type, acl); ++ } else { ++ acl = err == -ENODATA ? NULL : ERR_PTR(err); ++ } ++ ++ __putname(buf); ++ ++ return acl; ++} ++ ++/* ++ * ntfs_get_acl ++ * ++ * inode_operations::get_acl ++ */ ++struct posix_acl *ntfs_get_acl(struct inode *inode, int type) ++{ ++ /* TODO: init_user_ns? */ ++ return ntfs_get_acl_ex(&init_user_ns, inode, type, 0); ++} ++ ++static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, ++ struct inode *inode, struct posix_acl *acl, ++ int type, int locked) ++{ ++ const char *name; ++ size_t size, name_len; ++ void *value = NULL; ++ int err = 0; ++ ++ if (S_ISLNK(inode->i_mode)) ++ return -EOPNOTSUPP; ++ ++ switch (type) { ++ case ACL_TYPE_ACCESS: ++ if (acl) { ++ umode_t mode = inode->i_mode; ++ ++ err = posix_acl_equiv_mode(acl, &mode); ++ if (err < 0) ++ return err; ++ ++ if (inode->i_mode != mode) { ++ inode->i_mode = mode; ++ mark_inode_dirty(inode); ++ } ++ ++ if (!err) { ++ /* ++ * acl can be exactly represented in the ++ * traditional file mode permission bits ++ */ ++ acl = NULL; ++ } ++ } ++ name = XATTR_NAME_POSIX_ACL_ACCESS; ++ name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; ++ break; ++ ++ case ACL_TYPE_DEFAULT: ++ if (!S_ISDIR(inode->i_mode)) ++ return acl ? -EACCES : 0; ++ name = XATTR_NAME_POSIX_ACL_DEFAULT; ++ name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if (!acl) { ++ size = 0; ++ value = NULL; ++ } else { ++ size = posix_acl_xattr_size(acl->a_count); ++ value = ntfs_malloc(size); ++ if (!value) ++ return -ENOMEM; ++ ++ err = posix_acl_to_xattr(mnt_userns, acl, value, size); ++ if (err < 0) ++ goto out; ++ } ++ ++ err = ntfs_set_ea(inode, name, name_len, value, size, ++ acl ? 0 : XATTR_REPLACE, locked); ++ if (!err) ++ set_cached_acl(inode, type, acl); ++ ++out: ++ ntfs_free(value); ++ ++ return err; ++} ++ ++/* ++ * ntfs_set_acl ++ * ++ * inode_operations::set_acl ++ */ ++int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, ++ struct posix_acl *acl, int type) ++{ ++ return ntfs_set_acl_ex(mnt_userns, inode, acl, type, 0); ++} ++ ++static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns, ++ struct inode *inode, int type, void *buffer, ++ size_t size) ++{ ++ struct posix_acl *acl; ++ int err; ++ ++ if (!(inode->i_sb->s_flags & SB_POSIXACL)) ++ return -EOPNOTSUPP; ++ ++ acl = ntfs_get_acl(inode, type); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ ++ if (!acl) ++ return -ENODATA; ++ ++ err = posix_acl_to_xattr(mnt_userns, acl, buffer, size); ++ ntfs_posix_acl_release(acl); ++ ++ return err; ++} ++ ++static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns, ++ struct inode *inode, int type, const void *value, ++ size_t size) ++{ ++ struct posix_acl *acl; ++ int err; ++ ++ if (!(inode->i_sb->s_flags & SB_POSIXACL)) ++ return -EOPNOTSUPP; ++ ++ if (!inode_owner_or_capable(mnt_userns, inode)) ++ return -EPERM; ++ ++ if (!value) { ++ acl = NULL; ++ } else { ++ acl = posix_acl_from_xattr(mnt_userns, value, size); ++ if (IS_ERR(acl)) ++ return PTR_ERR(acl); ++ ++ if (acl) { ++ err = posix_acl_valid(mnt_userns, acl); ++ if (err) ++ goto release_and_out; ++ } ++ } ++ ++ err = ntfs_set_acl(mnt_userns, inode, acl, type); ++ ++release_and_out: ++ ntfs_posix_acl_release(acl); ++ return err; ++} ++ ++/* ++ * Initialize the ACLs of a new inode. Called from ntfs_create_inode. ++ */ ++int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, ++ struct inode *dir) ++{ ++ struct posix_acl *default_acl, *acl; ++ int err; ++ ++ /* ++ * TODO refactoring lock ++ * ni_lock(dir) ... -> posix_acl_create(dir,...) -> ntfs_get_acl -> ni_lock(dir) ++ */ ++ inode->i_default_acl = NULL; ++ ++ default_acl = ntfs_get_acl_ex(mnt_userns, dir, ACL_TYPE_DEFAULT, 1); ++ ++ if (!default_acl || default_acl == ERR_PTR(-EOPNOTSUPP)) { ++ inode->i_mode &= ~current_umask(); ++ err = 0; ++ goto out; ++ } ++ ++ if (IS_ERR(default_acl)) { ++ err = PTR_ERR(default_acl); ++ goto out; ++ } ++ ++ acl = default_acl; ++ err = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); ++ if (err < 0) ++ goto out1; ++ if (!err) { ++ posix_acl_release(acl); ++ acl = NULL; ++ } ++ ++ if (!S_ISDIR(inode->i_mode)) { ++ posix_acl_release(default_acl); ++ default_acl = NULL; ++ } ++ ++ if (default_acl) ++ err = ntfs_set_acl_ex(mnt_userns, inode, default_acl, ++ ACL_TYPE_DEFAULT, 1); ++ ++ if (!acl) ++ inode->i_acl = NULL; ++ else if (!err) ++ err = ntfs_set_acl_ex(mnt_userns, inode, acl, ACL_TYPE_ACCESS, ++ 1); ++ ++ posix_acl_release(acl); ++out1: ++ posix_acl_release(default_acl); ++ ++out: ++ return err; ++} ++#endif ++ ++/* ++ * ntfs_acl_chmod ++ * ++ * helper for 'ntfs3_setattr' ++ */ ++int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) ++{ ++ struct super_block *sb = inode->i_sb; ++ ++ if (!(sb->s_flags & SB_POSIXACL)) ++ return 0; ++ ++ if (S_ISLNK(inode->i_mode)) ++ return -EOPNOTSUPP; ++ ++ return posix_acl_chmod(mnt_userns, inode, inode->i_mode); ++} ++ ++/* ++ * ntfs_permission ++ * ++ * inode_operations::permission ++ */ ++int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, ++ int mask) ++{ ++ if (ntfs_sb(inode->i_sb)->options.no_acs_rules) { ++ /* "no access rules" mode - allow all changes */ ++ return 0; ++ } ++ ++ return generic_permission(mnt_userns, inode, mask); ++} ++ ++/* ++ * ntfs_listxattr ++ * ++ * inode_operations::listxattr ++ */ ++ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) ++{ ++ struct inode *inode = d_inode(dentry); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ ssize_t ret; ++ ++ if (!(ni->ni_flags & NI_FLAG_EA)) { ++ /* no xattr in file */ ++ return 0; ++ } ++ ++ ni_lock(ni); ++ ++ ret = ntfs_list_ea(ni, buffer, size); ++ ++ ni_unlock(ni); ++ ++ return ret; ++} ++ ++static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, ++ struct inode *inode, const char *name, void *buffer, ++ size_t size) ++{ ++ int err; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ size_t name_len = strlen(name); ++ ++ /* Dispatch request */ ++ if (name_len == sizeof(SYSTEM_DOS_ATTRIB) - 1 && ++ !memcmp(name, SYSTEM_DOS_ATTRIB, sizeof(SYSTEM_DOS_ATTRIB))) { ++ /* system.dos_attrib */ ++ if (!buffer) { ++ err = sizeof(u8); ++ } else if (size < sizeof(u8)) { ++ err = -ENODATA; ++ } else { ++ err = sizeof(u8); ++ *(u8 *)buffer = le32_to_cpu(ni->std_fa); ++ } ++ goto out; ++ } ++ ++ if (name_len == sizeof(SYSTEM_NTFS_ATTRIB) - 1 && ++ !memcmp(name, SYSTEM_NTFS_ATTRIB, sizeof(SYSTEM_NTFS_ATTRIB))) { ++ /* system.ntfs_attrib */ ++ if (!buffer) { ++ err = sizeof(u32); ++ } else if (size < sizeof(u32)) { ++ err = -ENODATA; ++ } else { ++ err = sizeof(u32); ++ *(u32 *)buffer = le32_to_cpu(ni->std_fa); ++ } ++ goto out; ++ } ++ ++ if (name_len == sizeof(SYSTEM_NTFS_SECURITY) - 1 && ++ !memcmp(name, SYSTEM_NTFS_SECURITY, sizeof(SYSTEM_NTFS_SECURITY))) { ++ /* system.ntfs_security*/ ++ struct SECURITY_DESCRIPTOR_RELATIVE *sd = NULL; ++ size_t sd_size = 0; ++ ++ if (!is_ntfs3(ni->mi.sbi)) { ++ /* we should get nt4 security */ ++ err = -EINVAL; ++ goto out; ++ } else if (le32_to_cpu(ni->std_security_id) < ++ SECURITY_ID_FIRST) { ++ err = -ENOENT; ++ goto out; ++ } ++ ++ err = ntfs_get_security_by_id(ni->mi.sbi, ni->std_security_id, ++ &sd, &sd_size); ++ if (err) ++ goto out; ++ ++ if (!is_sd_valid(sd, sd_size)) { ++ ntfs_inode_warn( ++ inode, ++ "looks like you get incorrect security descriptor id=%u", ++ ni->std_security_id); ++ } ++ ++ if (!buffer) { ++ err = sd_size; ++ } else if (size < sd_size) { ++ err = -ENODATA; ++ } else { ++ err = sd_size; ++ memcpy(buffer, sd, sd_size); ++ } ++ ntfs_free(sd); ++ goto out; ++ } ++ ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++ if ((name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 && ++ !memcmp(name, XATTR_NAME_POSIX_ACL_ACCESS, ++ sizeof(XATTR_NAME_POSIX_ACL_ACCESS))) || ++ (name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 && ++ !memcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, ++ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)))) { ++ /* TODO: init_user_ns? */ ++ err = ntfs_xattr_get_acl( ++ &init_user_ns, inode, ++ name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 ++ ? ACL_TYPE_ACCESS ++ : ACL_TYPE_DEFAULT, ++ buffer, size); ++ goto out; ++ } ++#endif ++ /* deal with ntfs extended attribute */ ++ err = ntfs_get_ea(inode, name, name_len, buffer, size, NULL); ++ ++out: ++ return err; ++} ++ ++/* ++ * ntfs_setxattr ++ * ++ * inode_operations::setxattr ++ */ ++static noinline int ntfs_setxattr(const struct xattr_handler *handler, ++ struct user_namespace *mnt_userns, ++ struct dentry *de, struct inode *inode, ++ const char *name, const void *value, ++ size_t size, int flags) ++{ ++ int err = -EINVAL; ++ struct ntfs_inode *ni = ntfs_i(inode); ++ size_t name_len = strlen(name); ++ enum FILE_ATTRIBUTE new_fa; ++ ++ /* Dispatch request */ ++ if (name_len == sizeof(SYSTEM_DOS_ATTRIB) - 1 && ++ !memcmp(name, SYSTEM_DOS_ATTRIB, sizeof(SYSTEM_DOS_ATTRIB))) { ++ if (sizeof(u8) != size) ++ goto out; ++ new_fa = cpu_to_le32(*(u8 *)value); ++ goto set_new_fa; ++ } ++ ++ if (name_len == sizeof(SYSTEM_NTFS_ATTRIB) - 1 && ++ !memcmp(name, SYSTEM_NTFS_ATTRIB, sizeof(SYSTEM_NTFS_ATTRIB))) { ++ if (size != sizeof(u32)) ++ goto out; ++ new_fa = cpu_to_le32(*(u32 *)value); ++ ++ if (S_ISREG(inode->i_mode)) { ++ /* Process compressed/sparsed in special way*/ ++ ni_lock(ni); ++ err = ni_new_attr_flags(ni, new_fa); ++ ni_unlock(ni); ++ if (err) ++ goto out; ++ } ++set_new_fa: ++ /* ++ * Thanks Mark Harmstone: ++ * keep directory bit consistency ++ */ ++ if (S_ISDIR(inode->i_mode)) ++ new_fa |= FILE_ATTRIBUTE_DIRECTORY; ++ else ++ new_fa &= ~FILE_ATTRIBUTE_DIRECTORY; ++ ++ if (ni->std_fa != new_fa) { ++ ni->std_fa = new_fa; ++ if (new_fa & FILE_ATTRIBUTE_READONLY) ++ inode->i_mode &= ~0222; ++ else ++ inode->i_mode |= 0222; ++ /* std attribute always in primary record */ ++ ni->mi.dirty = true; ++ mark_inode_dirty(inode); ++ } ++ err = 0; ++ ++ goto out; ++ } ++ ++ if (name_len == sizeof(SYSTEM_NTFS_SECURITY) - 1 && ++ !memcmp(name, SYSTEM_NTFS_SECURITY, sizeof(SYSTEM_NTFS_SECURITY))) { ++ /* system.ntfs_security*/ ++ __le32 security_id; ++ bool inserted; ++ struct ATTR_STD_INFO5 *std; ++ ++ if (!is_ntfs3(ni->mi.sbi)) { ++ /* ++ * we should replace ATTR_SECURE ++ * Skip this way cause it is nt4 feature ++ */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (!is_sd_valid(value, size)) { ++ err = -EINVAL; ++ ntfs_inode_warn( ++ inode, ++ "you try to set invalid security descriptor"); ++ goto out; ++ } ++ ++ err = ntfs_insert_security(ni->mi.sbi, value, size, ++ &security_id, &inserted); ++ if (err) ++ goto out; ++ ++ ni_lock(ni); ++ std = ni_std5(ni); ++ if (!std) { ++ err = -EINVAL; ++ } else if (std->security_id != security_id) { ++ std->security_id = ni->std_security_id = security_id; ++ /* std attribute always in primary record */ ++ ni->mi.dirty = true; ++ mark_inode_dirty(&ni->vfs_inode); ++ } ++ ni_unlock(ni); ++ goto out; ++ } ++ ++#ifdef CONFIG_NTFS3_FS_POSIX_ACL ++ if ((name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 && ++ !memcmp(name, XATTR_NAME_POSIX_ACL_ACCESS, ++ sizeof(XATTR_NAME_POSIX_ACL_ACCESS))) || ++ (name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 && ++ !memcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, ++ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)))) { ++ err = ntfs_xattr_set_acl( ++ mnt_userns, inode, ++ name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 ++ ? ACL_TYPE_ACCESS ++ : ACL_TYPE_DEFAULT, ++ value, size); ++ goto out; ++ } ++#endif ++ /* deal with ntfs extended attribute */ ++ err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0); ++ ++out: ++ return err; ++} ++ ++/* ++ * ntfs_save_wsl_perm ++ * ++ * save uid/gid/mode in xattr ++ */ ++int ntfs_save_wsl_perm(struct inode *inode) ++{ ++ int err; ++ __le32 value; ++ ++ value = cpu_to_le32(i_uid_read(inode)); ++ err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value, ++ sizeof(value), 0, 0); ++ if (err) ++ goto out; ++ ++ value = cpu_to_le32(i_gid_read(inode)); ++ err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value, ++ sizeof(value), 0, 0); ++ if (err) ++ goto out; ++ ++ value = cpu_to_le32(inode->i_mode); ++ err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value, ++ sizeof(value), 0, 0); ++ if (err) ++ goto out; ++ ++ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { ++ value = cpu_to_le32(inode->i_rdev); ++ err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value, ++ sizeof(value), 0, 0); ++ if (err) ++ goto out; ++ } ++ ++out: ++ /* In case of error should we delete all WSL xattr? */ ++ return err; ++} ++ ++/* ++ * ntfs_get_wsl_perm ++ * ++ * get uid/gid/mode from xattr ++ * it is called from ntfs_iget5->ntfs_read_mft ++ */ ++void ntfs_get_wsl_perm(struct inode *inode) ++{ ++ size_t sz; ++ __le32 value[3]; ++ ++ if (ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value[0], ++ sizeof(value[0]), &sz) == sizeof(value[0]) && ++ ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value[1], ++ sizeof(value[1]), &sz) == sizeof(value[1]) && ++ ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value[2], ++ sizeof(value[2]), &sz) == sizeof(value[2])) { ++ i_uid_write(inode, (uid_t)le32_to_cpu(value[0])); ++ i_gid_write(inode, (gid_t)le32_to_cpu(value[1])); ++ inode->i_mode = le32_to_cpu(value[2]); ++ ++ if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1, ++ &value[0], sizeof(value), ++ &sz) == sizeof(value[0])) { ++ inode->i_rdev = le32_to_cpu(value[0]); ++ } ++ } ++} ++ ++static bool ntfs_xattr_user_list(struct dentry *dentry) ++{ ++ return true; ++} ++ ++// clang-format off ++static const struct xattr_handler ntfs_xattr_handler = { ++ .prefix = "", ++ .get = ntfs_getxattr, ++ .set = ntfs_setxattr, ++ .list = ntfs_xattr_user_list, ++}; ++ ++const struct xattr_handler *ntfs_xattr_handlers[] = { ++ &ntfs_xattr_handler, ++ NULL, ++}; ++// clang-format on +-- +2.31.1 + diff --git a/SOURCES/0006-fs-ntfs3-Add-compression.patch b/SOURCES/0006-fs-ntfs3-Add-compression.patch new file mode 100644 index 0000000..ad55487 --- /dev/null +++ b/SOURCES/0006-fs-ntfs3-Add-compression.patch @@ -0,0 +1,2069 @@ +From 522e010b58379fbe19b38fdef5016bca0c3cf405 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:30 +0300 +Subject: [Backport 522e010b5837] src: Add compression + +This patch adds different types of NTFS-applicable compressions: +- lznt +- lzx +- xpress +Latter two (lzx, xpress) implement Windows Compact OS feature and +were taken from ntfs-3g system comression plugin authored by Eric Biggers +(https://github.com/ebiggers/ntfs-3g-system-compression) +which were ported to ntfs3 and adapted to Linux Kernel environment. + +Signed-off-by: Konstantin Komarov +--- + src/lib/decompress_common.c | 332 +++++++++++++++ + src/lib/decompress_common.h | 352 ++++++++++++++++ + src/lib/lib.h | 26 ++ + src/lib/lzx_decompress.c | 683 +++++++++++++++++++++++++++++++ + src/lib/xpress_decompress.c | 155 +++++++ + src/lznt.c | 452 ++++++++++++++++++++ + 6 files changed, 2000 insertions(+) + create mode 100644 src/lib/decompress_common.c + create mode 100644 src/lib/decompress_common.h + create mode 100644 src/lib/lib.h + create mode 100644 src/lib/lzx_decompress.c + create mode 100644 src/lib/xpress_decompress.c + create mode 100644 src/lznt.c + +diff --git a/src/lib/decompress_common.c b/src/lib/decompress_common.c +new file mode 100644 +index 0000000000000000000000000000000000000000..83c9e93aea77e437b5b1889b49272eee64d4df64 +--- /dev/null ++++ b/src/lib/decompress_common.c +@@ -0,0 +1,332 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * decompress_common.c - Code shared by the XPRESS and LZX decompressors ++ * ++ * Copyright (C) 2015 Eric Biggers ++ * ++ * This program is free software: you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License as published by the Free Software ++ * Foundation, either version 2 of the License, or (at your option) any later ++ * version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ++ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ++ * details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "decompress_common.h" ++ ++/* ++ * make_huffman_decode_table() - ++ * ++ * Build a decoding table for a canonical prefix code, or "Huffman code". ++ * ++ * This is an internal function, not part of the library API! ++ * ++ * This takes as input the length of the codeword for each symbol in the ++ * alphabet and produces as output a table that can be used for fast ++ * decoding of prefix-encoded symbols using read_huffsym(). ++ * ++ * Strictly speaking, a canonical prefix code might not be a Huffman ++ * code. But this algorithm will work either way; and in fact, since ++ * Huffman codes are defined in terms of symbol frequencies, there is no ++ * way for the decompressor to know whether the code is a true Huffman ++ * code or not until all symbols have been decoded. ++ * ++ * Because the prefix code is assumed to be "canonical", it can be ++ * reconstructed directly from the codeword lengths. A prefix code is ++ * canonical if and only if a longer codeword never lexicographically ++ * precedes a shorter codeword, and the lexicographic ordering of ++ * codewords of the same length is the same as the lexicographic ordering ++ * of the corresponding symbols. Consequently, we can sort the symbols ++ * primarily by codeword length and secondarily by symbol value, then ++ * reconstruct the prefix code by generating codewords lexicographically ++ * in that order. ++ * ++ * This function does not, however, generate the prefix code explicitly. ++ * Instead, it directly builds a table for decoding symbols using the ++ * code. The basic idea is this: given the next 'max_codeword_len' bits ++ * in the input, we can look up the decoded symbol by indexing a table ++ * containing 2**max_codeword_len entries. A codeword with length ++ * 'max_codeword_len' will have exactly one entry in this table, whereas ++ * a codeword shorter than 'max_codeword_len' will have multiple entries ++ * in this table. Precisely, a codeword of length n will be represented ++ * by 2**(max_codeword_len - n) entries in this table. The 0-based index ++ * of each such entry will contain the corresponding codeword as a prefix ++ * when zero-padded on the left to 'max_codeword_len' binary digits. ++ * ++ * That's the basic idea, but we implement two optimizations regarding ++ * the format of the decode table itself: ++ * ++ * - For many compression formats, the maximum codeword length is too ++ * long for it to be efficient to build the full decoding table ++ * whenever a new prefix code is used. Instead, we can build the table ++ * using only 2**table_bits entries, where 'table_bits' is some number ++ * less than or equal to 'max_codeword_len'. Then, only codewords of ++ * length 'table_bits' and shorter can be directly looked up. For ++ * longer codewords, the direct lookup instead produces the root of a ++ * binary tree. Using this tree, the decoder can do traditional ++ * bit-by-bit decoding of the remainder of the codeword. Child nodes ++ * are allocated in extra entries at the end of the table; leaf nodes ++ * contain symbols. Note that the long-codeword case is, in general, ++ * not performance critical, since in Huffman codes the most frequently ++ * used symbols are assigned the shortest codeword lengths. ++ * ++ * - When we decode a symbol using a direct lookup of the table, we still ++ * need to know its length so that the bitstream can be advanced by the ++ * appropriate number of bits. The simple solution is to simply retain ++ * the 'lens' array and use the decoded symbol as an index into it. ++ * However, this requires two separate array accesses in the fast path. ++ * The optimization is to store the length directly in the decode ++ * table. We use the bottom 11 bits for the symbol and the top 5 bits ++ * for the length. In addition, to combine this optimization with the ++ * previous one, we introduce a special case where the top 2 bits of ++ * the length are both set if the entry is actually the root of a ++ * binary tree. ++ * ++ * @decode_table: ++ * The array in which to create the decoding table. This must have ++ * a length of at least ((2**table_bits) + 2 * num_syms) entries. ++ * ++ * @num_syms: ++ * The number of symbols in the alphabet; also, the length of the ++ * 'lens' array. Must be less than or equal to 2048. ++ * ++ * @table_bits: ++ * The order of the decode table size, as explained above. Must be ++ * less than or equal to 13. ++ * ++ * @lens: ++ * An array of length @num_syms, indexable by symbol, that gives the ++ * length of the codeword, in bits, for that symbol. The length can ++ * be 0, which means that the symbol does not have a codeword ++ * assigned. ++ * ++ * @max_codeword_len: ++ * The longest codeword length allowed in the compression format. ++ * All entries in 'lens' must be less than or equal to this value. ++ * This must be less than or equal to 23. ++ * ++ * @working_space ++ * A temporary array of length '2 * (max_codeword_len + 1) + ++ * num_syms'. ++ * ++ * Returns 0 on success, or -1 if the lengths do not form a valid prefix ++ * code. ++ */ ++int make_huffman_decode_table(u16 decode_table[], const u32 num_syms, ++ const u32 table_bits, const u8 lens[], ++ const u32 max_codeword_len, ++ u16 working_space[]) ++{ ++ const u32 table_num_entries = 1 << table_bits; ++ u16 * const len_counts = &working_space[0]; ++ u16 * const offsets = &working_space[1 * (max_codeword_len + 1)]; ++ u16 * const sorted_syms = &working_space[2 * (max_codeword_len + 1)]; ++ int left; ++ void *decode_table_ptr; ++ u32 sym_idx; ++ u32 codeword_len; ++ u32 stores_per_loop; ++ u32 decode_table_pos; ++ u32 len; ++ u32 sym; ++ ++ /* Count how many symbols have each possible codeword length. ++ * Note that a length of 0 indicates the corresponding symbol is not ++ * used in the code and therefore does not have a codeword. ++ */ ++ for (len = 0; len <= max_codeword_len; len++) ++ len_counts[len] = 0; ++ for (sym = 0; sym < num_syms; sym++) ++ len_counts[lens[sym]]++; ++ ++ /* We can assume all lengths are <= max_codeword_len, but we ++ * cannot assume they form a valid prefix code. A codeword of ++ * length n should require a proportion of the codespace equaling ++ * (1/2)^n. The code is valid if and only if the codespace is ++ * exactly filled by the lengths, by this measure. ++ */ ++ left = 1; ++ for (len = 1; len <= max_codeword_len; len++) { ++ left <<= 1; ++ left -= len_counts[len]; ++ if (left < 0) { ++ /* The lengths overflow the codespace; that is, the code ++ * is over-subscribed. ++ */ ++ return -1; ++ } ++ } ++ ++ if (left) { ++ /* The lengths do not fill the codespace; that is, they form an ++ * incomplete set. ++ */ ++ if (left == (1 << max_codeword_len)) { ++ /* The code is completely empty. This is arguably ++ * invalid, but in fact it is valid in LZX and XPRESS, ++ * so we must allow it. By definition, no symbols can ++ * be decoded with an empty code. Consequently, we ++ * technically don't even need to fill in the decode ++ * table. However, to avoid accessing uninitialized ++ * memory if the algorithm nevertheless attempts to ++ * decode symbols using such a code, we zero out the ++ * decode table. ++ */ ++ memset(decode_table, 0, ++ table_num_entries * sizeof(decode_table[0])); ++ return 0; ++ } ++ return -1; ++ } ++ ++ /* Sort the symbols primarily by length and secondarily by symbol order. ++ */ ++ ++ /* Initialize 'offsets' so that offsets[len] for 1 <= len <= ++ * max_codeword_len is the number of codewords shorter than 'len' bits. ++ */ ++ offsets[1] = 0; ++ for (len = 1; len < max_codeword_len; len++) ++ offsets[len + 1] = offsets[len] + len_counts[len]; ++ ++ /* Use the 'offsets' array to sort the symbols. Note that we do not ++ * include symbols that are not used in the code. Consequently, fewer ++ * than 'num_syms' entries in 'sorted_syms' may be filled. ++ */ ++ for (sym = 0; sym < num_syms; sym++) ++ if (lens[sym]) ++ sorted_syms[offsets[lens[sym]]++] = sym; ++ ++ /* Fill entries for codewords with length <= table_bits ++ * --- that is, those short enough for a direct mapping. ++ * ++ * The table will start with entries for the shortest codeword(s), which ++ * have the most entries. From there, the number of entries per ++ * codeword will decrease. ++ */ ++ decode_table_ptr = decode_table; ++ sym_idx = 0; ++ codeword_len = 1; ++ stores_per_loop = (1 << (table_bits - codeword_len)); ++ for (; stores_per_loop != 0; codeword_len++, stores_per_loop >>= 1) { ++ u32 end_sym_idx = sym_idx + len_counts[codeword_len]; ++ ++ for (; sym_idx < end_sym_idx; sym_idx++) { ++ u16 entry; ++ u16 *p; ++ u32 n; ++ ++ entry = ((u32)codeword_len << 11) | sorted_syms[sym_idx]; ++ p = (u16 *)decode_table_ptr; ++ n = stores_per_loop; ++ ++ do { ++ *p++ = entry; ++ } while (--n); ++ ++ decode_table_ptr = p; ++ } ++ } ++ ++ /* If we've filled in the entire table, we are done. Otherwise, ++ * there are codewords longer than table_bits for which we must ++ * generate binary trees. ++ */ ++ decode_table_pos = (u16 *)decode_table_ptr - decode_table; ++ if (decode_table_pos != table_num_entries) { ++ u32 j; ++ u32 next_free_tree_slot; ++ u32 cur_codeword; ++ ++ /* First, zero out the remaining entries. This is ++ * necessary so that these entries appear as ++ * "unallocated" in the next part. Each of these entries ++ * will eventually be filled with the representation of ++ * the root node of a binary tree. ++ */ ++ j = decode_table_pos; ++ do { ++ decode_table[j] = 0; ++ } while (++j != table_num_entries); ++ ++ /* We allocate child nodes starting at the end of the ++ * direct lookup table. Note that there should be ++ * 2*num_syms extra entries for this purpose, although ++ * fewer than this may actually be needed. ++ */ ++ next_free_tree_slot = table_num_entries; ++ ++ /* Iterate through each codeword with length greater than ++ * 'table_bits', primarily in order of codeword length ++ * and secondarily in order of symbol. ++ */ ++ for (cur_codeword = decode_table_pos << 1; ++ codeword_len <= max_codeword_len; ++ codeword_len++, cur_codeword <<= 1) { ++ u32 end_sym_idx = sym_idx + len_counts[codeword_len]; ++ ++ for (; sym_idx < end_sym_idx; sym_idx++, cur_codeword++) { ++ /* 'sorted_sym' is the symbol represented by the ++ * codeword. ++ */ ++ u32 sorted_sym = sorted_syms[sym_idx]; ++ u32 extra_bits = codeword_len - table_bits; ++ u32 node_idx = cur_codeword >> extra_bits; ++ ++ /* Go through each bit of the current codeword ++ * beyond the prefix of length @table_bits and ++ * walk the appropriate binary tree, allocating ++ * any slots that have not yet been allocated. ++ * ++ * Note that the 'pointer' entry to the binary ++ * tree, which is stored in the direct lookup ++ * portion of the table, is represented ++ * identically to other internal (non-leaf) ++ * nodes of the binary tree; it can be thought ++ * of as simply the root of the tree. The ++ * representation of these internal nodes is ++ * simply the index of the left child combined ++ * with the special bits 0xC000 to distingush ++ * the entry from direct mapping and leaf node ++ * entries. ++ */ ++ do { ++ /* At least one bit remains in the ++ * codeword, but the current node is an ++ * unallocated leaf. Change it to an ++ * internal node. ++ */ ++ if (decode_table[node_idx] == 0) { ++ decode_table[node_idx] = ++ next_free_tree_slot | 0xC000; ++ decode_table[next_free_tree_slot++] = 0; ++ decode_table[next_free_tree_slot++] = 0; ++ } ++ ++ /* Go to the left child if the next bit ++ * in the codeword is 0; otherwise go to ++ * the right child. ++ */ ++ node_idx = decode_table[node_idx] & 0x3FFF; ++ --extra_bits; ++ node_idx += (cur_codeword >> extra_bits) & 1; ++ } while (extra_bits != 0); ++ ++ /* We've traversed the tree using the entire ++ * codeword, and we're now at the entry where ++ * the actual symbol will be stored. This is ++ * distinguished from internal nodes by not ++ * having its high two bits set. ++ */ ++ decode_table[node_idx] = sorted_sym; ++ } ++ } ++ } ++ return 0; ++} +diff --git a/src/lib/decompress_common.h b/src/lib/decompress_common.h +new file mode 100644 +index 0000000000000000000000000000000000000000..66297f398403f13abe05f3b5af1aa7c5674351e8 +--- /dev/null ++++ b/src/lib/decompress_common.h +@@ -0,0 +1,352 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++/* ++ * decompress_common.h - Code shared by the XPRESS and LZX decompressors ++ * ++ * Copyright (C) 2015 Eric Biggers ++ * ++ * This program is free software: you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License as published by the Free Software ++ * Foundation, either version 2 of the License, or (at your option) any later ++ * version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ++ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ++ * details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* "Force inline" macro (not required, but helpful for performance) */ ++#define forceinline __always_inline ++ ++/* Enable whole-word match copying on selected architectures */ ++#if defined(__i386__) || defined(__x86_64__) || defined(__ARM_FEATURE_UNALIGNED) ++# define FAST_UNALIGNED_ACCESS ++#endif ++ ++/* Size of a machine word */ ++#define WORDBYTES (sizeof(size_t)) ++ ++static forceinline void ++copy_unaligned_word(const void *src, void *dst) ++{ ++ put_unaligned(get_unaligned((const size_t *)src), (size_t *)dst); ++} ++ ++ ++/* Generate a "word" with platform-dependent size whose bytes all contain the ++ * value 'b'. ++ */ ++static forceinline size_t repeat_byte(u8 b) ++{ ++ size_t v; ++ ++ v = b; ++ v |= v << 8; ++ v |= v << 16; ++ v |= v << ((WORDBYTES == 8) ? 32 : 0); ++ return v; ++} ++ ++/* Structure that encapsulates a block of in-memory data being interpreted as a ++ * stream of bits, optionally with interwoven literal bytes. Bits are assumed ++ * to be stored in little endian 16-bit coding units, with the bits ordered high ++ * to low. ++ */ ++struct input_bitstream { ++ ++ /* Bits that have been read from the input buffer. The bits are ++ * left-justified; the next bit is always bit 31. ++ */ ++ u32 bitbuf; ++ ++ /* Number of bits currently held in @bitbuf. */ ++ u32 bitsleft; ++ ++ /* Pointer to the next byte to be retrieved from the input buffer. */ ++ const u8 *next; ++ ++ /* Pointer to just past the end of the input buffer. */ ++ const u8 *end; ++}; ++ ++/* Initialize a bitstream to read from the specified input buffer. */ ++static forceinline void init_input_bitstream(struct input_bitstream *is, ++ const void *buffer, u32 size) ++{ ++ is->bitbuf = 0; ++ is->bitsleft = 0; ++ is->next = buffer; ++ is->end = is->next + size; ++} ++ ++/* Ensure the bit buffer variable for the bitstream contains at least @num_bits ++ * bits. Following this, bitstream_peek_bits() and/or bitstream_remove_bits() ++ * may be called on the bitstream to peek or remove up to @num_bits bits. Note ++ * that @num_bits must be <= 16. ++ */ ++static forceinline void bitstream_ensure_bits(struct input_bitstream *is, ++ u32 num_bits) ++{ ++ if (is->bitsleft < num_bits) { ++ if (is->end - is->next >= 2) { ++ is->bitbuf |= (u32)get_unaligned_le16(is->next) ++ << (16 - is->bitsleft); ++ is->next += 2; ++ } ++ is->bitsleft += 16; ++ } ++} ++ ++/* Return the next @num_bits bits from the bitstream, without removing them. ++ * There must be at least @num_bits remaining in the buffer variable, from a ++ * previous call to bitstream_ensure_bits(). ++ */ ++static forceinline u32 ++bitstream_peek_bits(const struct input_bitstream *is, const u32 num_bits) ++{ ++ return (is->bitbuf >> 1) >> (sizeof(is->bitbuf) * 8 - num_bits - 1); ++} ++ ++/* Remove @num_bits from the bitstream. There must be at least @num_bits ++ * remaining in the buffer variable, from a previous call to ++ * bitstream_ensure_bits(). ++ */ ++static forceinline void ++bitstream_remove_bits(struct input_bitstream *is, u32 num_bits) ++{ ++ is->bitbuf <<= num_bits; ++ is->bitsleft -= num_bits; ++} ++ ++/* Remove and return @num_bits bits from the bitstream. There must be at least ++ * @num_bits remaining in the buffer variable, from a previous call to ++ * bitstream_ensure_bits(). ++ */ ++static forceinline u32 ++bitstream_pop_bits(struct input_bitstream *is, u32 num_bits) ++{ ++ u32 bits = bitstream_peek_bits(is, num_bits); ++ ++ bitstream_remove_bits(is, num_bits); ++ return bits; ++} ++ ++/* Read and return the next @num_bits bits from the bitstream. */ ++static forceinline u32 ++bitstream_read_bits(struct input_bitstream *is, u32 num_bits) ++{ ++ bitstream_ensure_bits(is, num_bits); ++ return bitstream_pop_bits(is, num_bits); ++} ++ ++/* Read and return the next literal byte embedded in the bitstream. */ ++static forceinline u8 ++bitstream_read_byte(struct input_bitstream *is) ++{ ++ if (unlikely(is->end == is->next)) ++ return 0; ++ return *is->next++; ++} ++ ++/* Read and return the next 16-bit integer embedded in the bitstream. */ ++static forceinline u16 ++bitstream_read_u16(struct input_bitstream *is) ++{ ++ u16 v; ++ ++ if (unlikely(is->end - is->next < 2)) ++ return 0; ++ v = get_unaligned_le16(is->next); ++ is->next += 2; ++ return v; ++} ++ ++/* Read and return the next 32-bit integer embedded in the bitstream. */ ++static forceinline u32 ++bitstream_read_u32(struct input_bitstream *is) ++{ ++ u32 v; ++ ++ if (unlikely(is->end - is->next < 4)) ++ return 0; ++ v = get_unaligned_le32(is->next); ++ is->next += 4; ++ return v; ++} ++ ++/* Read into @dst_buffer an array of literal bytes embedded in the bitstream. ++ * Return either a pointer to the byte past the last written, or NULL if the ++ * read overflows the input buffer. ++ */ ++static forceinline void *bitstream_read_bytes(struct input_bitstream *is, ++ void *dst_buffer, size_t count) ++{ ++ if ((size_t)(is->end - is->next) < count) ++ return NULL; ++ memcpy(dst_buffer, is->next, count); ++ is->next += count; ++ return (u8 *)dst_buffer + count; ++} ++ ++/* Align the input bitstream on a coding-unit boundary. */ ++static forceinline void bitstream_align(struct input_bitstream *is) ++{ ++ is->bitsleft = 0; ++ is->bitbuf = 0; ++} ++ ++extern int make_huffman_decode_table(u16 decode_table[], const u32 num_syms, ++ const u32 num_bits, const u8 lens[], ++ const u32 max_codeword_len, ++ u16 working_space[]); ++ ++ ++/* Reads and returns the next Huffman-encoded symbol from a bitstream. If the ++ * input data is exhausted, the Huffman symbol is decoded as if the missing bits ++ * are all zeroes. ++ */ ++static forceinline u32 read_huffsym(struct input_bitstream *istream, ++ const u16 decode_table[], ++ u32 table_bits, ++ u32 max_codeword_len) ++{ ++ u32 entry; ++ u32 key_bits; ++ ++ bitstream_ensure_bits(istream, max_codeword_len); ++ ++ /* Index the decode table by the next table_bits bits of the input. */ ++ key_bits = bitstream_peek_bits(istream, table_bits); ++ entry = decode_table[key_bits]; ++ if (entry < 0xC000) { ++ /* Fast case: The decode table directly provided the ++ * symbol and codeword length. The low 11 bits are the ++ * symbol, and the high 5 bits are the codeword length. ++ */ ++ bitstream_remove_bits(istream, entry >> 11); ++ return entry & 0x7FF; ++ } ++ /* Slow case: The codeword for the symbol is longer than ++ * table_bits, so the symbol does not have an entry ++ * directly in the first (1 << table_bits) entries of the ++ * decode table. Traverse the appropriate binary tree ++ * bit-by-bit to decode the symbol. ++ */ ++ bitstream_remove_bits(istream, table_bits); ++ do { ++ key_bits = (entry & 0x3FFF) + bitstream_pop_bits(istream, 1); ++ } while ((entry = decode_table[key_bits]) >= 0xC000); ++ return entry; ++} ++ ++/* ++ * Copy an LZ77 match at (dst - offset) to dst. ++ * ++ * The length and offset must be already validated --- that is, (dst - offset) ++ * can't underrun the output buffer, and (dst + length) can't overrun the output ++ * buffer. Also, the length cannot be 0. ++ * ++ * @bufend points to the byte past the end of the output buffer. This function ++ * won't write any data beyond this position. ++ * ++ * Returns dst + length. ++ */ ++static forceinline u8 *lz_copy(u8 *dst, u32 length, u32 offset, const u8 *bufend, ++ u32 min_length) ++{ ++ const u8 *src = dst - offset; ++ ++ /* ++ * Try to copy one machine word at a time. On i386 and x86_64 this is ++ * faster than copying one byte at a time, unless the data is ++ * near-random and all the matches have very short lengths. Note that ++ * since this requires unaligned memory accesses, it won't necessarily ++ * be faster on every architecture. ++ * ++ * Also note that we might copy more than the length of the match. For ++ * example, if a word is 8 bytes and the match is of length 5, then ++ * we'll simply copy 8 bytes. This is okay as long as we don't write ++ * beyond the end of the output buffer, hence the check for (bufend - ++ * end >= WORDBYTES - 1). ++ */ ++#ifdef FAST_UNALIGNED_ACCESS ++ u8 * const end = dst + length; ++ ++ if (bufend - end >= (ptrdiff_t)(WORDBYTES - 1)) { ++ ++ if (offset >= WORDBYTES) { ++ /* The source and destination words don't overlap. */ ++ ++ /* To improve branch prediction, one iteration of this ++ * loop is unrolled. Most matches are short and will ++ * fail the first check. But if that check passes, then ++ * it becomes increasing likely that the match is long ++ * and we'll need to continue copying. ++ */ ++ ++ copy_unaligned_word(src, dst); ++ src += WORDBYTES; ++ dst += WORDBYTES; ++ ++ if (dst < end) { ++ do { ++ copy_unaligned_word(src, dst); ++ src += WORDBYTES; ++ dst += WORDBYTES; ++ } while (dst < end); ++ } ++ return end; ++ } else if (offset == 1) { ++ ++ /* Offset 1 matches are equivalent to run-length ++ * encoding of the previous byte. This case is common ++ * if the data contains many repeated bytes. ++ */ ++ size_t v = repeat_byte(*(dst - 1)); ++ ++ do { ++ put_unaligned(v, (size_t *)dst); ++ src += WORDBYTES; ++ dst += WORDBYTES; ++ } while (dst < end); ++ return end; ++ } ++ /* ++ * We don't bother with special cases for other 'offset < ++ * WORDBYTES', which are usually rarer than 'offset == 1'. Extra ++ * checks will just slow things down. Actually, it's possible ++ * to handle all the 'offset < WORDBYTES' cases using the same ++ * code, but it still becomes more complicated doesn't seem any ++ * faster overall; it definitely slows down the more common ++ * 'offset == 1' case. ++ */ ++ } ++#endif /* FAST_UNALIGNED_ACCESS */ ++ ++ /* Fall back to a bytewise copy. */ ++ ++ if (min_length >= 2) { ++ *dst++ = *src++; ++ length--; ++ } ++ if (min_length >= 3) { ++ *dst++ = *src++; ++ length--; ++ } ++ do { ++ *dst++ = *src++; ++ } while (--length); ++ ++ return dst; ++} +diff --git a/src/lib/lib.h b/src/lib/lib.h +new file mode 100644 +index 0000000000000000000000000000000000000000..f508fbad2e712d946274b13f0ec7b244dc264a4d +--- /dev/null ++++ b/src/lib/lib.h +@@ -0,0 +1,26 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Adapted for linux kernel by Alexander Mamaev: ++ * - remove implementations of get_unaligned_ ++ * - assume GCC is always defined ++ * - ISO C90 ++ * - linux kernel code style ++ */ ++ ++ ++/* globals from xpress_decompress.c */ ++struct xpress_decompressor *xpress_allocate_decompressor(void); ++void xpress_free_decompressor(struct xpress_decompressor *d); ++int xpress_decompress(struct xpress_decompressor *__restrict d, ++ const void *__restrict compressed_data, ++ size_t compressed_size, ++ void *__restrict uncompressed_data, ++ size_t uncompressed_size); ++ ++/* globals from lzx_decompress.c */ ++struct lzx_decompressor *lzx_allocate_decompressor(void); ++void lzx_free_decompressor(struct lzx_decompressor *d); ++int lzx_decompress(struct lzx_decompressor *__restrict d, ++ const void *__restrict compressed_data, ++ size_t compressed_size, void *__restrict uncompressed_data, ++ size_t uncompressed_size); +diff --git a/src/lib/lzx_decompress.c b/src/lib/lzx_decompress.c +new file mode 100644 +index 0000000000000000000000000000000000000000..77a381a693d117e3e8c4860130880fedf3af868e +--- /dev/null ++++ b/src/lib/lzx_decompress.c +@@ -0,0 +1,683 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * lzx_decompress.c - A decompressor for the LZX compression format, which can ++ * be used in "System Compressed" files. This is based on the code from wimlib. ++ * This code only supports a window size (dictionary size) of 32768 bytes, since ++ * this is the only size used in System Compression. ++ * ++ * Copyright (C) 2015 Eric Biggers ++ * ++ * This program is free software: you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License as published by the Free Software ++ * Foundation, either version 2 of the License, or (at your option) any later ++ * version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ++ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ++ * details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "decompress_common.h" ++#include "lib.h" ++ ++/* Number of literal byte values */ ++#define LZX_NUM_CHARS 256 ++ ++/* The smallest and largest allowed match lengths */ ++#define LZX_MIN_MATCH_LEN 2 ++#define LZX_MAX_MATCH_LEN 257 ++ ++/* Number of distinct match lengths that can be represented */ ++#define LZX_NUM_LENS (LZX_MAX_MATCH_LEN - LZX_MIN_MATCH_LEN + 1) ++ ++/* Number of match lengths for which no length symbol is required */ ++#define LZX_NUM_PRIMARY_LENS 7 ++#define LZX_NUM_LEN_HEADERS (LZX_NUM_PRIMARY_LENS + 1) ++ ++/* Valid values of the 3-bit block type field */ ++#define LZX_BLOCKTYPE_VERBATIM 1 ++#define LZX_BLOCKTYPE_ALIGNED 2 ++#define LZX_BLOCKTYPE_UNCOMPRESSED 3 ++ ++/* Number of offset slots for a window size of 32768 */ ++#define LZX_NUM_OFFSET_SLOTS 30 ++ ++/* Number of symbols in the main code for a window size of 32768 */ ++#define LZX_MAINCODE_NUM_SYMBOLS \ ++ (LZX_NUM_CHARS + (LZX_NUM_OFFSET_SLOTS * LZX_NUM_LEN_HEADERS)) ++ ++/* Number of symbols in the length code */ ++#define LZX_LENCODE_NUM_SYMBOLS (LZX_NUM_LENS - LZX_NUM_PRIMARY_LENS) ++ ++/* Number of symbols in the precode */ ++#define LZX_PRECODE_NUM_SYMBOLS 20 ++ ++/* Number of bits in which each precode codeword length is represented */ ++#define LZX_PRECODE_ELEMENT_SIZE 4 ++ ++/* Number of low-order bits of each match offset that are entropy-encoded in ++ * aligned offset blocks ++ */ ++#define LZX_NUM_ALIGNED_OFFSET_BITS 3 ++ ++/* Number of symbols in the aligned offset code */ ++#define LZX_ALIGNEDCODE_NUM_SYMBOLS (1 << LZX_NUM_ALIGNED_OFFSET_BITS) ++ ++/* Mask for the match offset bits that are entropy-encoded in aligned offset ++ * blocks ++ */ ++#define LZX_ALIGNED_OFFSET_BITMASK ((1 << LZX_NUM_ALIGNED_OFFSET_BITS) - 1) ++ ++/* Number of bits in which each aligned offset codeword length is represented */ ++#define LZX_ALIGNEDCODE_ELEMENT_SIZE 3 ++ ++/* Maximum lengths (in bits) of the codewords in each Huffman code */ ++#define LZX_MAX_MAIN_CODEWORD_LEN 16 ++#define LZX_MAX_LEN_CODEWORD_LEN 16 ++#define LZX_MAX_PRE_CODEWORD_LEN ((1 << LZX_PRECODE_ELEMENT_SIZE) - 1) ++#define LZX_MAX_ALIGNED_CODEWORD_LEN ((1 << LZX_ALIGNEDCODE_ELEMENT_SIZE) - 1) ++ ++/* The default "filesize" value used in pre/post-processing. In the LZX format ++ * used in cabinet files this value must be given to the decompressor, whereas ++ * in the LZX format used in WIM files and system-compressed files this value is ++ * fixed at 12000000. ++ */ ++#define LZX_DEFAULT_FILESIZE 12000000 ++ ++/* Assumed block size when the encoded block size begins with a 0 bit. */ ++#define LZX_DEFAULT_BLOCK_SIZE 32768 ++ ++/* Number of offsets in the recent (or "repeat") offsets queue. */ ++#define LZX_NUM_RECENT_OFFSETS 3 ++ ++/* These values are chosen for fast decompression. */ ++#define LZX_MAINCODE_TABLEBITS 11 ++#define LZX_LENCODE_TABLEBITS 10 ++#define LZX_PRECODE_TABLEBITS 6 ++#define LZX_ALIGNEDCODE_TABLEBITS 7 ++ ++#define LZX_READ_LENS_MAX_OVERRUN 50 ++ ++/* Mapping: offset slot => first match offset that uses that offset slot. ++ */ ++static const u32 lzx_offset_slot_base[LZX_NUM_OFFSET_SLOTS + 1] = { ++ 0, 1, 2, 3, 4, /* 0 --- 4 */ ++ 6, 8, 12, 16, 24, /* 5 --- 9 */ ++ 32, 48, 64, 96, 128, /* 10 --- 14 */ ++ 192, 256, 384, 512, 768, /* 15 --- 19 */ ++ 1024, 1536, 2048, 3072, 4096, /* 20 --- 24 */ ++ 6144, 8192, 12288, 16384, 24576, /* 25 --- 29 */ ++ 32768, /* extra */ ++}; ++ ++/* Mapping: offset slot => how many extra bits must be read and added to the ++ * corresponding offset slot base to decode the match offset. ++ */ ++static const u8 lzx_extra_offset_bits[LZX_NUM_OFFSET_SLOTS] = { ++ 0, 0, 0, 0, 1, ++ 1, 2, 2, 3, 3, ++ 4, 4, 5, 5, 6, ++ 6, 7, 7, 8, 8, ++ 9, 9, 10, 10, 11, ++ 11, 12, 12, 13, 13, ++}; ++ ++/* Reusable heap-allocated memory for LZX decompression */ ++struct lzx_decompressor { ++ ++ /* Huffman decoding tables, and arrays that map symbols to codeword ++ * lengths ++ */ ++ ++ u16 maincode_decode_table[(1 << LZX_MAINCODE_TABLEBITS) + ++ (LZX_MAINCODE_NUM_SYMBOLS * 2)]; ++ u8 maincode_lens[LZX_MAINCODE_NUM_SYMBOLS + LZX_READ_LENS_MAX_OVERRUN]; ++ ++ ++ u16 lencode_decode_table[(1 << LZX_LENCODE_TABLEBITS) + ++ (LZX_LENCODE_NUM_SYMBOLS * 2)]; ++ u8 lencode_lens[LZX_LENCODE_NUM_SYMBOLS + LZX_READ_LENS_MAX_OVERRUN]; ++ ++ ++ u16 alignedcode_decode_table[(1 << LZX_ALIGNEDCODE_TABLEBITS) + ++ (LZX_ALIGNEDCODE_NUM_SYMBOLS * 2)]; ++ u8 alignedcode_lens[LZX_ALIGNEDCODE_NUM_SYMBOLS]; ++ ++ u16 precode_decode_table[(1 << LZX_PRECODE_TABLEBITS) + ++ (LZX_PRECODE_NUM_SYMBOLS * 2)]; ++ u8 precode_lens[LZX_PRECODE_NUM_SYMBOLS]; ++ ++ /* Temporary space for make_huffman_decode_table() */ ++ u16 working_space[2 * (1 + LZX_MAX_MAIN_CODEWORD_LEN) + ++ LZX_MAINCODE_NUM_SYMBOLS]; ++}; ++ ++static void undo_e8_translation(void *target, s32 input_pos) ++{ ++ s32 abs_offset, rel_offset; ++ ++ abs_offset = get_unaligned_le32(target); ++ if (abs_offset >= 0) { ++ if (abs_offset < LZX_DEFAULT_FILESIZE) { ++ /* "good translation" */ ++ rel_offset = abs_offset - input_pos; ++ put_unaligned_le32(rel_offset, target); ++ } ++ } else { ++ if (abs_offset >= -input_pos) { ++ /* "compensating translation" */ ++ rel_offset = abs_offset + LZX_DEFAULT_FILESIZE; ++ put_unaligned_le32(rel_offset, target); ++ } ++ } ++} ++ ++/* ++ * Undo the 'E8' preprocessing used in LZX. Before compression, the ++ * uncompressed data was preprocessed by changing the targets of suspected x86 ++ * CALL instructions from relative offsets to absolute offsets. After ++ * match/literal decoding, the decompressor must undo the translation. ++ */ ++static void lzx_postprocess(u8 *data, u32 size) ++{ ++ /* ++ * A worthwhile optimization is to push the end-of-buffer check into the ++ * relatively rare E8 case. This is possible if we replace the last six ++ * bytes of data with E8 bytes; then we are guaranteed to hit an E8 byte ++ * before reaching end-of-buffer. In addition, this scheme guarantees ++ * that no translation can begin following an E8 byte in the last 10 ++ * bytes because a 4-byte offset containing E8 as its high byte is a ++ * large negative number that is not valid for translation. That is ++ * exactly what we need. ++ */ ++ u8 *tail; ++ u8 saved_bytes[6]; ++ u8 *p; ++ ++ if (size <= 10) ++ return; ++ ++ tail = &data[size - 6]; ++ memcpy(saved_bytes, tail, 6); ++ memset(tail, 0xE8, 6); ++ p = data; ++ for (;;) { ++ while (*p != 0xE8) ++ p++; ++ if (p >= tail) ++ break; ++ undo_e8_translation(p + 1, p - data); ++ p += 5; ++ } ++ memcpy(tail, saved_bytes, 6); ++} ++ ++/* Read a Huffman-encoded symbol using the precode. */ ++static forceinline u32 read_presym(const struct lzx_decompressor *d, ++ struct input_bitstream *is) ++{ ++ return read_huffsym(is, d->precode_decode_table, ++ LZX_PRECODE_TABLEBITS, LZX_MAX_PRE_CODEWORD_LEN); ++} ++ ++/* Read a Huffman-encoded symbol using the main code. */ ++static forceinline u32 read_mainsym(const struct lzx_decompressor *d, ++ struct input_bitstream *is) ++{ ++ return read_huffsym(is, d->maincode_decode_table, ++ LZX_MAINCODE_TABLEBITS, LZX_MAX_MAIN_CODEWORD_LEN); ++} ++ ++/* Read a Huffman-encoded symbol using the length code. */ ++static forceinline u32 read_lensym(const struct lzx_decompressor *d, ++ struct input_bitstream *is) ++{ ++ return read_huffsym(is, d->lencode_decode_table, ++ LZX_LENCODE_TABLEBITS, LZX_MAX_LEN_CODEWORD_LEN); ++} ++ ++/* Read a Huffman-encoded symbol using the aligned offset code. */ ++static forceinline u32 read_alignedsym(const struct lzx_decompressor *d, ++ struct input_bitstream *is) ++{ ++ return read_huffsym(is, d->alignedcode_decode_table, ++ LZX_ALIGNEDCODE_TABLEBITS, ++ LZX_MAX_ALIGNED_CODEWORD_LEN); ++} ++ ++/* ++ * Read the precode from the compressed input bitstream, then use it to decode ++ * @num_lens codeword length values. ++ * ++ * @is: The input bitstream. ++ * ++ * @lens: An array that contains the length values from the previous time ++ * the codeword lengths for this Huffman code were read, or all 0's ++ * if this is the first time. This array must have at least ++ * (@num_lens + LZX_READ_LENS_MAX_OVERRUN) entries. ++ * ++ * @num_lens: Number of length values to decode. ++ * ++ * Returns 0 on success, or -1 if the data was invalid. ++ */ ++static int lzx_read_codeword_lens(struct lzx_decompressor *d, ++ struct input_bitstream *is, ++ u8 *lens, u32 num_lens) ++{ ++ u8 *len_ptr = lens; ++ u8 *lens_end = lens + num_lens; ++ int i; ++ ++ /* Read the lengths of the precode codewords. These are given ++ * explicitly. ++ */ ++ for (i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++) { ++ d->precode_lens[i] = ++ bitstream_read_bits(is, LZX_PRECODE_ELEMENT_SIZE); ++ } ++ ++ /* Make the decoding table for the precode. */ ++ if (make_huffman_decode_table(d->precode_decode_table, ++ LZX_PRECODE_NUM_SYMBOLS, ++ LZX_PRECODE_TABLEBITS, ++ d->precode_lens, ++ LZX_MAX_PRE_CODEWORD_LEN, ++ d->working_space)) ++ return -1; ++ ++ /* Decode the codeword lengths. */ ++ do { ++ u32 presym; ++ u8 len; ++ ++ /* Read the next precode symbol. */ ++ presym = read_presym(d, is); ++ if (presym < 17) { ++ /* Difference from old length */ ++ len = *len_ptr - presym; ++ if ((s8)len < 0) ++ len += 17; ++ *len_ptr++ = len; ++ } else { ++ /* Special RLE values */ ++ ++ u32 run_len; ++ ++ if (presym == 17) { ++ /* Run of 0's */ ++ run_len = 4 + bitstream_read_bits(is, 4); ++ len = 0; ++ } else if (presym == 18) { ++ /* Longer run of 0's */ ++ run_len = 20 + bitstream_read_bits(is, 5); ++ len = 0; ++ } else { ++ /* Run of identical lengths */ ++ run_len = 4 + bitstream_read_bits(is, 1); ++ presym = read_presym(d, is); ++ if (presym > 17) ++ return -1; ++ len = *len_ptr - presym; ++ if ((s8)len < 0) ++ len += 17; ++ } ++ ++ do { ++ *len_ptr++ = len; ++ } while (--run_len); ++ /* Worst case overrun is when presym == 18, ++ * run_len == 20 + 31, and only 1 length was remaining. ++ * So LZX_READ_LENS_MAX_OVERRUN == 50. ++ * ++ * Overrun while reading the first half of maincode_lens ++ * can corrupt the previous values in the second half. ++ * This doesn't really matter because the resulting ++ * lengths will still be in range, and data that ++ * generates overruns is invalid anyway. ++ */ ++ } ++ } while (len_ptr < lens_end); ++ ++ return 0; ++} ++ ++/* ++ * Read the header of an LZX block and save the block type and (uncompressed) ++ * size in *block_type_ret and *block_size_ret, respectively. ++ * ++ * If the block is compressed, also update the Huffman decode @tables with the ++ * new Huffman codes. If the block is uncompressed, also update the match ++ * offset @queue with the new match offsets. ++ * ++ * Return 0 on success, or -1 if the data was invalid. ++ */ ++static int lzx_read_block_header(struct lzx_decompressor *d, ++ struct input_bitstream *is, ++ int *block_type_ret, ++ u32 *block_size_ret, ++ u32 recent_offsets[]) ++{ ++ int block_type; ++ u32 block_size; ++ int i; ++ ++ bitstream_ensure_bits(is, 4); ++ ++ /* The first three bits tell us what kind of block it is, and should be ++ * one of the LZX_BLOCKTYPE_* values. ++ */ ++ block_type = bitstream_pop_bits(is, 3); ++ ++ /* Read the block size. */ ++ if (bitstream_pop_bits(is, 1)) { ++ block_size = LZX_DEFAULT_BLOCK_SIZE; ++ } else { ++ block_size = 0; ++ block_size |= bitstream_read_bits(is, 8); ++ block_size <<= 8; ++ block_size |= bitstream_read_bits(is, 8); ++ } ++ ++ switch (block_type) { ++ ++ case LZX_BLOCKTYPE_ALIGNED: ++ ++ /* Read the aligned offset code and prepare its decode table. ++ */ ++ ++ for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) { ++ d->alignedcode_lens[i] = ++ bitstream_read_bits(is, ++ LZX_ALIGNEDCODE_ELEMENT_SIZE); ++ } ++ ++ if (make_huffman_decode_table(d->alignedcode_decode_table, ++ LZX_ALIGNEDCODE_NUM_SYMBOLS, ++ LZX_ALIGNEDCODE_TABLEBITS, ++ d->alignedcode_lens, ++ LZX_MAX_ALIGNED_CODEWORD_LEN, ++ d->working_space)) ++ return -1; ++ ++ /* Fall though, since the rest of the header for aligned offset ++ * blocks is the same as that for verbatim blocks. ++ */ ++ fallthrough; ++ ++ case LZX_BLOCKTYPE_VERBATIM: ++ ++ /* Read the main code and prepare its decode table. ++ * ++ * Note that the codeword lengths in the main code are encoded ++ * in two parts: one part for literal symbols, and one part for ++ * match symbols. ++ */ ++ ++ if (lzx_read_codeword_lens(d, is, d->maincode_lens, ++ LZX_NUM_CHARS)) ++ return -1; ++ ++ if (lzx_read_codeword_lens(d, is, ++ d->maincode_lens + LZX_NUM_CHARS, ++ LZX_MAINCODE_NUM_SYMBOLS - LZX_NUM_CHARS)) ++ return -1; ++ ++ if (make_huffman_decode_table(d->maincode_decode_table, ++ LZX_MAINCODE_NUM_SYMBOLS, ++ LZX_MAINCODE_TABLEBITS, ++ d->maincode_lens, ++ LZX_MAX_MAIN_CODEWORD_LEN, ++ d->working_space)) ++ return -1; ++ ++ /* Read the length code and prepare its decode table. */ ++ ++ if (lzx_read_codeword_lens(d, is, d->lencode_lens, ++ LZX_LENCODE_NUM_SYMBOLS)) ++ return -1; ++ ++ if (make_huffman_decode_table(d->lencode_decode_table, ++ LZX_LENCODE_NUM_SYMBOLS, ++ LZX_LENCODE_TABLEBITS, ++ d->lencode_lens, ++ LZX_MAX_LEN_CODEWORD_LEN, ++ d->working_space)) ++ return -1; ++ ++ break; ++ ++ case LZX_BLOCKTYPE_UNCOMPRESSED: ++ ++ /* Before reading the three recent offsets from the uncompressed ++ * block header, the stream must be aligned on a 16-bit ++ * boundary. But if the stream is *already* aligned, then the ++ * next 16 bits must be discarded. ++ */ ++ bitstream_ensure_bits(is, 1); ++ bitstream_align(is); ++ ++ recent_offsets[0] = bitstream_read_u32(is); ++ recent_offsets[1] = bitstream_read_u32(is); ++ recent_offsets[2] = bitstream_read_u32(is); ++ ++ /* Offsets of 0 are invalid. */ ++ if (recent_offsets[0] == 0 || recent_offsets[1] == 0 || ++ recent_offsets[2] == 0) ++ return -1; ++ break; ++ ++ default: ++ /* Unrecognized block type. */ ++ return -1; ++ } ++ ++ *block_type_ret = block_type; ++ *block_size_ret = block_size; ++ return 0; ++} ++ ++/* Decompress a block of LZX-compressed data. */ ++static int lzx_decompress_block(const struct lzx_decompressor *d, ++ struct input_bitstream *is, ++ int block_type, u32 block_size, ++ u8 * const out_begin, u8 *out_next, ++ u32 recent_offsets[]) ++{ ++ u8 * const block_end = out_next + block_size; ++ u32 ones_if_aligned = 0U - (block_type == LZX_BLOCKTYPE_ALIGNED); ++ ++ do { ++ u32 mainsym; ++ u32 match_len; ++ u32 match_offset; ++ u32 offset_slot; ++ u32 num_extra_bits; ++ ++ mainsym = read_mainsym(d, is); ++ if (mainsym < LZX_NUM_CHARS) { ++ /* Literal */ ++ *out_next++ = mainsym; ++ continue; ++ } ++ ++ /* Match */ ++ ++ /* Decode the length header and offset slot. */ ++ mainsym -= LZX_NUM_CHARS; ++ match_len = mainsym % LZX_NUM_LEN_HEADERS; ++ offset_slot = mainsym / LZX_NUM_LEN_HEADERS; ++ ++ /* If needed, read a length symbol to decode the full length. */ ++ if (match_len == LZX_NUM_PRIMARY_LENS) ++ match_len += read_lensym(d, is); ++ match_len += LZX_MIN_MATCH_LEN; ++ ++ if (offset_slot < LZX_NUM_RECENT_OFFSETS) { ++ /* Repeat offset */ ++ ++ /* Note: This isn't a real LRU queue, since using the R2 ++ * offset doesn't bump the R1 offset down to R2. This ++ * quirk allows all 3 recent offsets to be handled by ++ * the same code. (For R0, the swap is a no-op.) ++ */ ++ match_offset = recent_offsets[offset_slot]; ++ recent_offsets[offset_slot] = recent_offsets[0]; ++ recent_offsets[0] = match_offset; ++ } else { ++ /* Explicit offset */ ++ ++ /* Look up the number of extra bits that need to be read ++ * to decode offsets with this offset slot. ++ */ ++ num_extra_bits = lzx_extra_offset_bits[offset_slot]; ++ ++ /* Start with the offset slot base value. */ ++ match_offset = lzx_offset_slot_base[offset_slot]; ++ ++ /* In aligned offset blocks, the low-order 3 bits of ++ * each offset are encoded using the aligned offset ++ * code. Otherwise, all the extra bits are literal. ++ */ ++ ++ if ((num_extra_bits & ones_if_aligned) >= LZX_NUM_ALIGNED_OFFSET_BITS) { ++ match_offset += ++ bitstream_read_bits(is, num_extra_bits - ++ LZX_NUM_ALIGNED_OFFSET_BITS) ++ << LZX_NUM_ALIGNED_OFFSET_BITS; ++ match_offset += read_alignedsym(d, is); ++ } else { ++ match_offset += bitstream_read_bits(is, num_extra_bits); ++ } ++ ++ /* Adjust the offset. */ ++ match_offset -= (LZX_NUM_RECENT_OFFSETS - 1); ++ ++ /* Update the recent offsets. */ ++ recent_offsets[2] = recent_offsets[1]; ++ recent_offsets[1] = recent_offsets[0]; ++ recent_offsets[0] = match_offset; ++ } ++ ++ /* Validate the match, then copy it to the current position. */ ++ ++ if (match_len > (size_t)(block_end - out_next)) ++ return -1; ++ ++ if (match_offset > (size_t)(out_next - out_begin)) ++ return -1; ++ ++ out_next = lz_copy(out_next, match_len, match_offset, ++ block_end, LZX_MIN_MATCH_LEN); ++ ++ } while (out_next != block_end); ++ ++ return 0; ++} ++ ++/* ++ * lzx_allocate_decompressor - Allocate an LZX decompressor ++ * ++ * Return the pointer to the decompressor on success, or return NULL and set ++ * errno on failure. ++ */ ++struct lzx_decompressor *lzx_allocate_decompressor(void) ++{ ++ return kmalloc(sizeof(struct lzx_decompressor), GFP_NOFS); ++} ++ ++/* ++ * lzx_decompress - Decompress a buffer of LZX-compressed data ++ * ++ * @decompressor: A decompressor allocated with lzx_allocate_decompressor() ++ * @compressed_data: The buffer of data to decompress ++ * @compressed_size: Number of bytes of compressed data ++ * @uncompressed_data: The buffer in which to store the decompressed data ++ * @uncompressed_size: The number of bytes the data decompresses into ++ * ++ * Return 0 on success, or return -1 and set errno on failure. ++ */ ++int lzx_decompress(struct lzx_decompressor *decompressor, ++ const void *compressed_data, size_t compressed_size, ++ void *uncompressed_data, size_t uncompressed_size) ++{ ++ struct lzx_decompressor *d = decompressor; ++ u8 * const out_begin = uncompressed_data; ++ u8 *out_next = out_begin; ++ u8 * const out_end = out_begin + uncompressed_size; ++ struct input_bitstream is; ++ u32 recent_offsets[LZX_NUM_RECENT_OFFSETS] = {1, 1, 1}; ++ int e8_status = 0; ++ ++ init_input_bitstream(&is, compressed_data, compressed_size); ++ ++ /* Codeword lengths begin as all 0's for delta encoding purposes. */ ++ memset(d->maincode_lens, 0, LZX_MAINCODE_NUM_SYMBOLS); ++ memset(d->lencode_lens, 0, LZX_LENCODE_NUM_SYMBOLS); ++ ++ /* Decompress blocks until we have all the uncompressed data. */ ++ ++ while (out_next != out_end) { ++ int block_type; ++ u32 block_size; ++ ++ if (lzx_read_block_header(d, &is, &block_type, &block_size, ++ recent_offsets)) ++ goto invalid; ++ ++ if (block_size < 1 || block_size > (size_t)(out_end - out_next)) ++ goto invalid; ++ ++ if (block_type != LZX_BLOCKTYPE_UNCOMPRESSED) { ++ ++ /* Compressed block */ ++ ++ if (lzx_decompress_block(d, ++ &is, ++ block_type, ++ block_size, ++ out_begin, ++ out_next, ++ recent_offsets)) ++ goto invalid; ++ ++ e8_status |= d->maincode_lens[0xe8]; ++ out_next += block_size; ++ } else { ++ /* Uncompressed block */ ++ ++ out_next = bitstream_read_bytes(&is, out_next, ++ block_size); ++ if (!out_next) ++ goto invalid; ++ ++ if (block_size & 1) ++ bitstream_read_byte(&is); ++ ++ e8_status = 1; ++ } ++ } ++ ++ /* Postprocess the data unless it cannot possibly contain 0xe8 bytes. */ ++ if (e8_status) ++ lzx_postprocess(uncompressed_data, uncompressed_size); ++ ++ return 0; ++ ++invalid: ++ return -1; ++} ++ ++/* ++ * lzx_free_decompressor - Free an LZX decompressor ++ * ++ * @decompressor: A decompressor that was allocated with ++ * lzx_allocate_decompressor(), or NULL. ++ */ ++void lzx_free_decompressor(struct lzx_decompressor *decompressor) ++{ ++ kfree(decompressor); ++} +diff --git a/src/lib/xpress_decompress.c b/src/lib/xpress_decompress.c +new file mode 100644 +index 0000000000000000000000000000000000000000..3d98f36a981e672d4d924b5fcbaf655d18d96355 +--- /dev/null ++++ b/src/lib/xpress_decompress.c +@@ -0,0 +1,155 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * xpress_decompress.c - A decompressor for the XPRESS compression format ++ * (Huffman variant), which can be used in "System Compressed" files. This is ++ * based on the code from wimlib. ++ * ++ * Copyright (C) 2015 Eric Biggers ++ * ++ * This program is free software: you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License as published by the Free Software ++ * Foundation, either version 2 of the License, or (at your option) any later ++ * version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ++ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ++ * details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "decompress_common.h" ++#include "lib.h" ++ ++#define XPRESS_NUM_SYMBOLS 512 ++#define XPRESS_MAX_CODEWORD_LEN 15 ++#define XPRESS_MIN_MATCH_LEN 3 ++ ++/* This value is chosen for fast decompression. */ ++#define XPRESS_TABLEBITS 12 ++ ++/* Reusable heap-allocated memory for XPRESS decompression */ ++struct xpress_decompressor { ++ ++ /* The Huffman decoding table */ ++ u16 decode_table[(1 << XPRESS_TABLEBITS) + 2 * XPRESS_NUM_SYMBOLS]; ++ ++ /* An array that maps symbols to codeword lengths */ ++ u8 lens[XPRESS_NUM_SYMBOLS]; ++ ++ /* Temporary space for make_huffman_decode_table() */ ++ u16 working_space[2 * (1 + XPRESS_MAX_CODEWORD_LEN) + ++ XPRESS_NUM_SYMBOLS]; ++}; ++ ++/* ++ * xpress_allocate_decompressor - Allocate an XPRESS decompressor ++ * ++ * Return the pointer to the decompressor on success, or return NULL and set ++ * errno on failure. ++ */ ++struct xpress_decompressor *xpress_allocate_decompressor(void) ++{ ++ return kmalloc(sizeof(struct xpress_decompressor), GFP_NOFS); ++} ++ ++/* ++ * xpress_decompress - Decompress a buffer of XPRESS-compressed data ++ * ++ * @decompressor: A decompressor that was allocated with ++ * xpress_allocate_decompressor() ++ * @compressed_data: The buffer of data to decompress ++ * @compressed_size: Number of bytes of compressed data ++ * @uncompressed_data: The buffer in which to store the decompressed data ++ * @uncompressed_size: The number of bytes the data decompresses into ++ * ++ * Return 0 on success, or return -1 and set errno on failure. ++ */ ++int xpress_decompress(struct xpress_decompressor *decompressor, ++ const void *compressed_data, size_t compressed_size, ++ void *uncompressed_data, size_t uncompressed_size) ++{ ++ struct xpress_decompressor *d = decompressor; ++ const u8 * const in_begin = compressed_data; ++ u8 * const out_begin = uncompressed_data; ++ u8 *out_next = out_begin; ++ u8 * const out_end = out_begin + uncompressed_size; ++ struct input_bitstream is; ++ u32 i; ++ ++ /* Read the Huffman codeword lengths. */ ++ if (compressed_size < XPRESS_NUM_SYMBOLS / 2) ++ goto invalid; ++ for (i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) { ++ d->lens[i*2 + 0] = in_begin[i] & 0xF; ++ d->lens[i*2 + 1] = in_begin[i] >> 4; ++ } ++ ++ /* Build a decoding table for the Huffman code. */ ++ if (make_huffman_decode_table(d->decode_table, XPRESS_NUM_SYMBOLS, ++ XPRESS_TABLEBITS, d->lens, ++ XPRESS_MAX_CODEWORD_LEN, ++ d->working_space)) ++ goto invalid; ++ ++ /* Decode the matches and literals. */ ++ ++ init_input_bitstream(&is, in_begin + XPRESS_NUM_SYMBOLS / 2, ++ compressed_size - XPRESS_NUM_SYMBOLS / 2); ++ ++ while (out_next != out_end) { ++ u32 sym; ++ u32 log2_offset; ++ u32 length; ++ u32 offset; ++ ++ sym = read_huffsym(&is, d->decode_table, ++ XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN); ++ if (sym < 256) { ++ /* Literal */ ++ *out_next++ = sym; ++ } else { ++ /* Match */ ++ length = sym & 0xf; ++ log2_offset = (sym >> 4) & 0xf; ++ ++ bitstream_ensure_bits(&is, 16); ++ ++ offset = ((u32)1 << log2_offset) | ++ bitstream_pop_bits(&is, log2_offset); ++ ++ if (length == 0xf) { ++ length += bitstream_read_byte(&is); ++ if (length == 0xf + 0xff) ++ length = bitstream_read_u16(&is); ++ } ++ length += XPRESS_MIN_MATCH_LEN; ++ ++ if (offset > (size_t)(out_next - out_begin)) ++ goto invalid; ++ ++ if (length > (size_t)(out_end - out_next)) ++ goto invalid; ++ ++ out_next = lz_copy(out_next, length, offset, out_end, ++ XPRESS_MIN_MATCH_LEN); ++ } ++ } ++ return 0; ++ ++invalid: ++ return -1; ++} ++ ++/* ++ * xpress_free_decompressor - Free an XPRESS decompressor ++ * ++ * @decompressor: A decompressor that was allocated with ++ * xpress_allocate_decompressor(), or NULL. ++ */ ++void xpress_free_decompressor(struct xpress_decompressor *decompressor) ++{ ++ kfree(decompressor); ++} +diff --git a/src/lznt.c b/src/lznt.c +new file mode 100644 +index 0000000000000000000000000000000000000000..ead9ab7d69b3082e429ac79b973aab461d3cfd7a +--- /dev/null ++++ b/src/lznt.c +@@ -0,0 +1,452 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++// clang-format off ++/* src buffer is zero */ ++#define LZNT_ERROR_ALL_ZEROS 1 ++#define LZNT_CHUNK_SIZE 0x1000 ++// clang-format on ++ ++struct lznt_hash { ++ const u8 *p1; ++ const u8 *p2; ++}; ++ ++struct lznt { ++ const u8 *unc; ++ const u8 *unc_end; ++ const u8 *best_match; ++ size_t max_len; ++ bool std; ++ ++ struct lznt_hash hash[LZNT_CHUNK_SIZE]; ++}; ++ ++static inline size_t get_match_len(const u8 *ptr, const u8 *end, const u8 *prev, ++ size_t max_len) ++{ ++ size_t len = 0; ++ ++ while (ptr + len < end && ptr[len] == prev[len] && ++len < max_len) ++ ; ++ return len; ++} ++ ++static size_t longest_match_std(const u8 *src, struct lznt *ctx) ++{ ++ size_t hash_index; ++ size_t len1 = 0, len2 = 0; ++ const u8 **hash; ++ ++ hash_index = ++ ((40543U * ((((src[0] << 4) ^ src[1]) << 4) ^ src[2])) >> 4) & ++ (LZNT_CHUNK_SIZE - 1); ++ ++ hash = &(ctx->hash[hash_index].p1); ++ ++ if (hash[0] >= ctx->unc && hash[0] < src && hash[0][0] == src[0] && ++ hash[0][1] == src[1] && hash[0][2] == src[2]) { ++ len1 = 3; ++ if (ctx->max_len > 3) ++ len1 += get_match_len(src + 3, ctx->unc_end, ++ hash[0] + 3, ctx->max_len - 3); ++ } ++ ++ if (hash[1] >= ctx->unc && hash[1] < src && hash[1][0] == src[0] && ++ hash[1][1] == src[1] && hash[1][2] == src[2]) { ++ len2 = 3; ++ if (ctx->max_len > 3) ++ len2 += get_match_len(src + 3, ctx->unc_end, ++ hash[1] + 3, ctx->max_len - 3); ++ } ++ ++ /* Compare two matches and select the best one */ ++ if (len1 < len2) { ++ ctx->best_match = hash[1]; ++ len1 = len2; ++ } else { ++ ctx->best_match = hash[0]; ++ } ++ ++ hash[1] = hash[0]; ++ hash[0] = src; ++ return len1; ++} ++ ++static size_t longest_match_best(const u8 *src, struct lznt *ctx) ++{ ++ size_t max_len; ++ const u8 *ptr; ++ ++ if (ctx->unc >= src || !ctx->max_len) ++ return 0; ++ ++ max_len = 0; ++ for (ptr = ctx->unc; ptr < src; ++ptr) { ++ size_t len = ++ get_match_len(src, ctx->unc_end, ptr, ctx->max_len); ++ if (len >= max_len) { ++ max_len = len; ++ ctx->best_match = ptr; ++ } ++ } ++ ++ return max_len >= 3 ? max_len : 0; ++} ++ ++static const size_t s_max_len[] = { ++ 0x1002, 0x802, 0x402, 0x202, 0x102, 0x82, 0x42, 0x22, 0x12, ++}; ++ ++static const size_t s_max_off[] = { ++ 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, ++}; ++ ++static inline u16 make_pair(size_t offset, size_t len, size_t index) ++{ ++ return ((offset - 1) << (12 - index)) | ++ ((len - 3) & (((1 << (12 - index)) - 1))); ++} ++ ++static inline size_t parse_pair(u16 pair, size_t *offset, size_t index) ++{ ++ *offset = 1 + (pair >> (12 - index)); ++ return 3 + (pair & ((1 << (12 - index)) - 1)); ++} ++ ++/* ++ * compress_chunk ++ * ++ * returns one of the three values: ++ * 0 - ok, 'cmpr' contains 'cmpr_chunk_size' bytes of compressed data ++ * 1 - input buffer is full zero ++ * -2 - the compressed buffer is too small to hold the compressed data ++ */ ++static inline int compress_chunk(size_t (*match)(const u8 *, struct lznt *), ++ const u8 *unc, const u8 *unc_end, u8 *cmpr, ++ u8 *cmpr_end, size_t *cmpr_chunk_size, ++ struct lznt *ctx) ++{ ++ size_t cnt = 0; ++ size_t idx = 0; ++ const u8 *up = unc; ++ u8 *cp = cmpr + 3; ++ u8 *cp2 = cmpr + 2; ++ u8 not_zero = 0; ++ /* Control byte of 8-bit values: ( 0 - means byte as is, 1 - short pair ) */ ++ u8 ohdr = 0; ++ u8 *last; ++ u16 t16; ++ ++ if (unc + LZNT_CHUNK_SIZE < unc_end) ++ unc_end = unc + LZNT_CHUNK_SIZE; ++ ++ last = min(cmpr + LZNT_CHUNK_SIZE + sizeof(short), cmpr_end); ++ ++ ctx->unc = unc; ++ ctx->unc_end = unc_end; ++ ctx->max_len = s_max_len[0]; ++ ++ while (up < unc_end) { ++ size_t max_len; ++ ++ while (unc + s_max_off[idx] < up) ++ ctx->max_len = s_max_len[++idx]; ++ ++ // Find match ++ max_len = up + 3 <= unc_end ? (*match)(up, ctx) : 0; ++ ++ if (!max_len) { ++ if (cp >= last) ++ goto NotCompressed; ++ not_zero |= *cp++ = *up++; ++ } else if (cp + 1 >= last) { ++ goto NotCompressed; ++ } else { ++ t16 = make_pair(up - ctx->best_match, max_len, idx); ++ *cp++ = t16; ++ *cp++ = t16 >> 8; ++ ++ ohdr |= 1 << cnt; ++ up += max_len; ++ } ++ ++ cnt = (cnt + 1) & 7; ++ if (!cnt) { ++ *cp2 = ohdr; ++ ohdr = 0; ++ cp2 = cp; ++ cp += 1; ++ } ++ } ++ ++ if (cp2 < last) ++ *cp2 = ohdr; ++ else ++ cp -= 1; ++ ++ *cmpr_chunk_size = cp - cmpr; ++ ++ t16 = (*cmpr_chunk_size - 3) | 0xB000; ++ cmpr[0] = t16; ++ cmpr[1] = t16 >> 8; ++ ++ return not_zero ? 0 : LZNT_ERROR_ALL_ZEROS; ++ ++NotCompressed: ++ ++ if ((cmpr + LZNT_CHUNK_SIZE + sizeof(short)) > last) ++ return -2; ++ ++ /* ++ * Copy non cmpr data ++ * 0x3FFF == ((LZNT_CHUNK_SIZE + 2 - 3) | 0x3000) ++ */ ++ cmpr[0] = 0xff; ++ cmpr[1] = 0x3f; ++ ++ memcpy(cmpr + sizeof(short), unc, LZNT_CHUNK_SIZE); ++ *cmpr_chunk_size = LZNT_CHUNK_SIZE + sizeof(short); ++ ++ return 0; ++} ++ ++static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, ++ const u8 *cmpr_end) ++{ ++ u8 *up = unc; ++ u8 ch = *cmpr++; ++ size_t bit = 0; ++ size_t index = 0; ++ u16 pair; ++ size_t offset, length; ++ ++ /* Do decompression until pointers are inside range */ ++ while (up < unc_end && cmpr < cmpr_end) { ++ /* Correct index */ ++ while (unc + s_max_off[index] < up) ++ index += 1; ++ ++ /* Check the current flag for zero */ ++ if (!(ch & (1 << bit))) { ++ /* Just copy byte */ ++ *up++ = *cmpr++; ++ goto next; ++ } ++ ++ /* Check for boundary */ ++ if (cmpr + 1 >= cmpr_end) ++ return -EINVAL; ++ ++ /* Read a short from little endian stream */ ++ pair = cmpr[1]; ++ pair <<= 8; ++ pair |= cmpr[0]; ++ ++ cmpr += 2; ++ ++ /* Translate packed information into offset and length */ ++ length = parse_pair(pair, &offset, index); ++ ++ /* Check offset for boundary */ ++ if (unc + offset > up) ++ return -EINVAL; ++ ++ /* Truncate the length if necessary */ ++ if (up + length >= unc_end) ++ length = unc_end - up; ++ ++ /* Now we copy bytes. This is the heart of LZ algorithm. */ ++ for (; length > 0; length--, up++) ++ *up = *(up - offset); ++ ++next: ++ /* Advance flag bit value */ ++ bit = (bit + 1) & 7; ++ ++ if (!bit) { ++ if (cmpr >= cmpr_end) ++ break; ++ ++ ch = *cmpr++; ++ } ++ } ++ ++ /* return the size of uncompressed data */ ++ return up - unc; ++} ++ ++/* ++ * 0 - standard compression ++ * !0 - best compression, requires a lot of cpu ++ */ ++struct lznt *get_lznt_ctx(int level) ++{ ++ struct lznt *r = ntfs_zalloc(level ? offsetof(struct lznt, hash) ++ : sizeof(struct lznt)); ++ ++ if (r) ++ r->std = !level; ++ return r; ++} ++ ++/* ++ * compress_lznt ++ * ++ * Compresses "unc" into "cmpr" ++ * +x - ok, 'cmpr' contains 'final_compressed_size' bytes of compressed data ++ * 0 - input buffer is full zero ++ */ ++size_t compress_lznt(const void *unc, size_t unc_size, void *cmpr, ++ size_t cmpr_size, struct lznt *ctx) ++{ ++ int err; ++ size_t (*match)(const u8 *src, struct lznt *ctx); ++ u8 *p = cmpr; ++ u8 *end = p + cmpr_size; ++ const u8 *unc_chunk = unc; ++ const u8 *unc_end = unc_chunk + unc_size; ++ bool is_zero = true; ++ ++ if (ctx->std) { ++ match = &longest_match_std; ++ memset(ctx->hash, 0, sizeof(ctx->hash)); ++ } else { ++ match = &longest_match_best; ++ } ++ ++ /* compression cycle */ ++ for (; unc_chunk < unc_end; unc_chunk += LZNT_CHUNK_SIZE) { ++ cmpr_size = 0; ++ err = compress_chunk(match, unc_chunk, unc_end, p, end, ++ &cmpr_size, ctx); ++ if (err < 0) ++ return unc_size; ++ ++ if (is_zero && err != LZNT_ERROR_ALL_ZEROS) ++ is_zero = false; ++ ++ p += cmpr_size; ++ } ++ ++ if (p <= end - 2) ++ p[0] = p[1] = 0; ++ ++ return is_zero ? 0 : PtrOffset(cmpr, p); ++} ++ ++/* ++ * decompress_lznt ++ * ++ * decompresses "cmpr" into "unc" ++ */ ++ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, ++ size_t unc_size) ++{ ++ const u8 *cmpr_chunk = cmpr; ++ const u8 *cmpr_end = cmpr_chunk + cmpr_size; ++ u8 *unc_chunk = unc; ++ u8 *unc_end = unc_chunk + unc_size; ++ u16 chunk_hdr; ++ ++ if (cmpr_size < sizeof(short)) ++ return -EINVAL; ++ ++ /* read chunk header */ ++ chunk_hdr = cmpr_chunk[1]; ++ chunk_hdr <<= 8; ++ chunk_hdr |= cmpr_chunk[0]; ++ ++ /* loop through decompressing chunks */ ++ for (;;) { ++ size_t chunk_size_saved; ++ size_t unc_use; ++ size_t cmpr_use = 3 + (chunk_hdr & (LZNT_CHUNK_SIZE - 1)); ++ ++ /* Check that the chunk actually fits the supplied buffer */ ++ if (cmpr_chunk + cmpr_use > cmpr_end) ++ return -EINVAL; ++ ++ /* First make sure the chunk contains compressed data */ ++ if (chunk_hdr & 0x8000) { ++ /* Decompress a chunk and return if we get an error */ ++ ssize_t err = ++ decompress_chunk(unc_chunk, unc_end, ++ cmpr_chunk + sizeof(chunk_hdr), ++ cmpr_chunk + cmpr_use); ++ if (err < 0) ++ return err; ++ unc_use = err; ++ } else { ++ /* This chunk does not contain compressed data */ ++ unc_use = unc_chunk + LZNT_CHUNK_SIZE > unc_end ++ ? unc_end - unc_chunk ++ : LZNT_CHUNK_SIZE; ++ ++ if (cmpr_chunk + sizeof(chunk_hdr) + unc_use > ++ cmpr_end) { ++ return -EINVAL; ++ } ++ ++ memcpy(unc_chunk, cmpr_chunk + sizeof(chunk_hdr), ++ unc_use); ++ } ++ ++ /* Advance pointers */ ++ cmpr_chunk += cmpr_use; ++ unc_chunk += unc_use; ++ ++ /* Check for the end of unc buffer */ ++ if (unc_chunk >= unc_end) ++ break; ++ ++ /* Proceed the next chunk */ ++ if (cmpr_chunk > cmpr_end - 2) ++ break; ++ ++ chunk_size_saved = LZNT_CHUNK_SIZE; ++ ++ /* read chunk header */ ++ chunk_hdr = cmpr_chunk[1]; ++ chunk_hdr <<= 8; ++ chunk_hdr |= cmpr_chunk[0]; ++ ++ if (!chunk_hdr) ++ break; ++ ++ /* Check the size of unc buffer */ ++ if (unc_use < chunk_size_saved) { ++ size_t t1 = chunk_size_saved - unc_use; ++ u8 *t2 = unc_chunk + t1; ++ ++ /* 'Zero' memory */ ++ if (t2 >= unc_end) ++ break; ++ ++ memset(unc_chunk, 0, t1); ++ unc_chunk = t2; ++ } ++ } ++ ++ /* Check compression boundary */ ++ if (cmpr_chunk > cmpr_end) ++ return -EINVAL; ++ ++ /* ++ * The unc size is just a difference between current ++ * pointer and original one ++ */ ++ return PtrOffset(unc, unc_chunk); ++} +-- +2.31.1 + diff --git a/SOURCES/0007-fs-ntfs3-Add-NTFS-journal.patch b/SOURCES/0007-fs-ntfs3-Add-NTFS-journal.patch new file mode 100644 index 0000000..9bd9f03 --- /dev/null +++ b/SOURCES/0007-fs-ntfs3-Add-NTFS-journal.patch @@ -0,0 +1,5204 @@ +From b46acd6a6a627d876898e1c84d3f84902264b445 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:30 +0300 +Subject: [Backport b46acd6a6a62] src: Add NTFS journal + +This adds NTFS journal + +Signed-off-by: Konstantin Komarov +--- + src/fslog.c | 5182 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 5182 insertions(+) + create mode 100644 src/fslog.c + +diff --git a/src/fslog.c b/src/fslog.c +new file mode 100644 +index 0000000000000000000000000000000000000000..397ba6a956e7f0e25661c88c0c91ec089c4d2103 +--- /dev/null ++++ b/src/fslog.c +@@ -0,0 +1,5182 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++#include "ntfs_fs.h" ++ ++/* ++ * LOG FILE structs ++ */ ++ ++// clang-format off ++ ++#define MaxLogFileSize 0x100000000ull ++#define DefaultLogPageSize 4096 ++#define MinLogRecordPages 0x30 ++ ++struct RESTART_HDR { ++ struct NTFS_RECORD_HEADER rhdr; // 'RSTR' ++ __le32 sys_page_size; // 0x10: Page size of the system which initialized the log ++ __le32 page_size; // 0x14: Log page size used for this log file ++ __le16 ra_off; // 0x18: ++ __le16 minor_ver; // 0x1A: ++ __le16 major_ver; // 0x1C: ++ __le16 fixups[]; ++}; ++ ++#define LFS_NO_CLIENT 0xffff ++#define LFS_NO_CLIENT_LE cpu_to_le16(0xffff) ++ ++struct CLIENT_REC { ++ __le64 oldest_lsn; ++ __le64 restart_lsn; // 0x08: ++ __le16 prev_client; // 0x10: ++ __le16 next_client; // 0x12: ++ __le16 seq_num; // 0x14: ++ u8 align[6]; // 0x16 ++ __le32 name_bytes; // 0x1C: in bytes ++ __le16 name[32]; // 0x20: name of client ++}; ++ ++static_assert(sizeof(struct CLIENT_REC) == 0x60); ++ ++/* Two copies of these will exist at the beginning of the log file */ ++struct RESTART_AREA { ++ __le64 current_lsn; // 0x00: Current logical end of log file ++ __le16 log_clients; // 0x08: Maximum number of clients ++ __le16 client_idx[2]; // 0x0A: free/use index into the client record arrays ++ __le16 flags; // 0x0E: See RESTART_SINGLE_PAGE_IO ++ __le32 seq_num_bits; // 0x10: the number of bits in sequence number. ++ __le16 ra_len; // 0x14: ++ __le16 client_off; // 0x16: ++ __le64 l_size; // 0x18: Usable log file size. ++ __le32 last_lsn_data_len; // 0x20: ++ __le16 rec_hdr_len; // 0x24: log page data offset ++ __le16 data_off; // 0x26: log page data length ++ __le32 open_log_count; // 0x28: ++ __le32 align[5]; // 0x2C: ++ struct CLIENT_REC clients[]; // 0x40: ++}; ++ ++struct LOG_REC_HDR { ++ __le16 redo_op; // 0x00: NTFS_LOG_OPERATION ++ __le16 undo_op; // 0x02: NTFS_LOG_OPERATION ++ __le16 redo_off; // 0x04: Offset to Redo record ++ __le16 redo_len; // 0x06: Redo length ++ __le16 undo_off; // 0x08: Offset to Undo record ++ __le16 undo_len; // 0x0A: Undo length ++ __le16 target_attr; // 0x0C: ++ __le16 lcns_follow; // 0x0E: ++ __le16 record_off; // 0x10: ++ __le16 attr_off; // 0x12: ++ __le16 cluster_off; // 0x14: ++ __le16 reserved; // 0x16: ++ __le64 target_vcn; // 0x18: ++ __le64 page_lcns[]; // 0x20: ++}; ++ ++static_assert(sizeof(struct LOG_REC_HDR) == 0x20); ++ ++#define RESTART_ENTRY_ALLOCATED 0xFFFFFFFF ++#define RESTART_ENTRY_ALLOCATED_LE cpu_to_le32(0xFFFFFFFF) ++ ++struct RESTART_TABLE { ++ __le16 size; // 0x00: In bytes ++ __le16 used; // 0x02: entries ++ __le16 total; // 0x04: entries ++ __le16 res[3]; // 0x06: ++ __le32 free_goal; // 0x0C: ++ __le32 first_free; // 0x10 ++ __le32 last_free; // 0x14 ++ ++}; ++ ++static_assert(sizeof(struct RESTART_TABLE) == 0x18); ++ ++struct ATTR_NAME_ENTRY { ++ __le16 off; // offset in the Open attribute Table ++ __le16 name_bytes; ++ __le16 name[]; ++}; ++ ++struct OPEN_ATTR_ENRTY { ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ __le32 bytes_per_index; // 0x04: ++ enum ATTR_TYPE type; // 0x08: ++ u8 is_dirty_pages; // 0x0C: ++ u8 is_attr_name; // 0x0B: Faked field to manage 'ptr' ++ u8 name_len; // 0x0C: Faked field to manage 'ptr' ++ u8 res; ++ struct MFT_REF ref; // 0x10: File Reference of file containing attribute ++ __le64 open_record_lsn; // 0x18: ++ void *ptr; // 0x20: ++}; ++ ++/* 32 bit version of 'struct OPEN_ATTR_ENRTY' */ ++struct OPEN_ATTR_ENRTY_32 { ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ __le32 ptr; // 0x04: ++ struct MFT_REF ref; // 0x08: ++ __le64 open_record_lsn; // 0x10: ++ u8 is_dirty_pages; // 0x18: ++ u8 is_attr_name; // 0x19 ++ u8 res1[2]; ++ enum ATTR_TYPE type; // 0x1C: ++ u8 name_len; // 0x20: in wchar ++ u8 res2[3]; ++ __le32 AttributeName; // 0x24: ++ __le32 bytes_per_index; // 0x28: ++}; ++ ++#define SIZEOF_OPENATTRIBUTEENTRY0 0x2c ++// static_assert( 0x2C == sizeof(struct OPEN_ATTR_ENRTY_32) ); ++static_assert(sizeof(struct OPEN_ATTR_ENRTY) < SIZEOF_OPENATTRIBUTEENTRY0); ++ ++/* ++ * One entry exists in the Dirty Pages Table for each page which is dirty at the ++ * time the Restart Area is written ++ */ ++struct DIR_PAGE_ENTRY { ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ __le32 target_attr; // 0x04: Index into the Open attribute Table ++ __le32 transfer_len; // 0x08: ++ __le32 lcns_follow; // 0x0C: ++ __le64 vcn; // 0x10: Vcn of dirty page ++ __le64 oldest_lsn; // 0x18: ++ __le64 page_lcns[]; // 0x20: ++}; ++ ++static_assert(sizeof(struct DIR_PAGE_ENTRY) == 0x20); ++ ++/* 32 bit version of 'struct DIR_PAGE_ENTRY' */ ++struct DIR_PAGE_ENTRY_32 { ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ __le32 target_attr; // 0x04: Index into the Open attribute Table ++ __le32 transfer_len; // 0x08: ++ __le32 lcns_follow; // 0x0C: ++ __le32 reserved; // 0x10: ++ __le32 vcn_low; // 0x14: Vcn of dirty page ++ __le32 vcn_hi; // 0x18: Vcn of dirty page ++ __le32 oldest_lsn_low; // 0x1C: ++ __le32 oldest_lsn_hi; // 0x1C: ++ __le32 page_lcns_low; // 0x24: ++ __le32 page_lcns_hi; // 0x24: ++}; ++ ++static_assert(offsetof(struct DIR_PAGE_ENTRY_32, vcn_low) == 0x14); ++static_assert(sizeof(struct DIR_PAGE_ENTRY_32) == 0x2c); ++ ++enum transact_state { ++ TransactionUninitialized = 0, ++ TransactionActive, ++ TransactionPrepared, ++ TransactionCommitted ++}; ++ ++struct TRANSACTION_ENTRY { ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ u8 transact_state; // 0x04: ++ u8 reserved[3]; // 0x05: ++ __le64 first_lsn; // 0x08: ++ __le64 prev_lsn; // 0x10: ++ __le64 undo_next_lsn; // 0x18: ++ __le32 undo_records; // 0x20: Number of undo log records pending abort ++ __le32 undo_len; // 0x24: Total undo size ++}; ++ ++static_assert(sizeof(struct TRANSACTION_ENTRY) == 0x28); ++ ++struct NTFS_RESTART { ++ __le32 major_ver; // 0x00: ++ __le32 minor_ver; // 0x04: ++ __le64 check_point_start; // 0x08: ++ __le64 open_attr_table_lsn; // 0x10: ++ __le64 attr_names_lsn; // 0x18: ++ __le64 dirty_pages_table_lsn; // 0x20: ++ __le64 transact_table_lsn; // 0x28: ++ __le32 open_attr_len; // 0x30: In bytes ++ __le32 attr_names_len; // 0x34: In bytes ++ __le32 dirty_pages_len; // 0x38: In bytes ++ __le32 transact_table_len; // 0x3C: In bytes ++}; ++ ++static_assert(sizeof(struct NTFS_RESTART) == 0x40); ++ ++struct NEW_ATTRIBUTE_SIZES { ++ __le64 alloc_size; ++ __le64 valid_size; ++ __le64 data_size; ++ __le64 total_size; ++}; ++ ++struct BITMAP_RANGE { ++ __le32 bitmap_off; ++ __le32 bits; ++}; ++ ++struct LCN_RANGE { ++ __le64 lcn; ++ __le64 len; ++}; ++ ++/* The following type defines the different log record types */ ++#define LfsClientRecord cpu_to_le32(1) ++#define LfsClientRestart cpu_to_le32(2) ++ ++/* This is used to uniquely identify a client for a particular log file */ ++struct CLIENT_ID { ++ __le16 seq_num; ++ __le16 client_idx; ++}; ++ ++/* This is the header that begins every Log Record in the log file */ ++struct LFS_RECORD_HDR { ++ __le64 this_lsn; // 0x00: ++ __le64 client_prev_lsn; // 0x08: ++ __le64 client_undo_next_lsn; // 0x10: ++ __le32 client_data_len; // 0x18: ++ struct CLIENT_ID client; // 0x1C: Owner of this log record ++ __le32 record_type; // 0x20: LfsClientRecord or LfsClientRestart ++ __le32 transact_id; // 0x24: ++ __le16 flags; // 0x28: LOG_RECORD_MULTI_PAGE ++ u8 align[6]; // 0x2A: ++}; ++ ++#define LOG_RECORD_MULTI_PAGE cpu_to_le16(1) ++ ++static_assert(sizeof(struct LFS_RECORD_HDR) == 0x30); ++ ++struct LFS_RECORD { ++ __le16 next_record_off; // 0x00: Offset of the free space in the page ++ u8 align[6]; // 0x02: ++ __le64 last_end_lsn; // 0x08: lsn for the last log record which ends on the page ++}; ++ ++static_assert(sizeof(struct LFS_RECORD) == 0x10); ++ ++struct RECORD_PAGE_HDR { ++ struct NTFS_RECORD_HEADER rhdr; // 'RCRD' ++ __le32 rflags; // 0x10: See LOG_PAGE_LOG_RECORD_END ++ __le16 page_count; // 0x14: ++ __le16 page_pos; // 0x16: ++ struct LFS_RECORD record_hdr; // 0x18 ++ __le16 fixups[10]; // 0x28 ++ __le32 file_off; // 0x3c: used when major version >= 2 ++}; ++ ++// clang-format on ++ ++// Page contains the end of a log record ++#define LOG_PAGE_LOG_RECORD_END cpu_to_le32(0x00000001) ++ ++static inline bool is_log_record_end(const struct RECORD_PAGE_HDR *hdr) ++{ ++ return hdr->rflags & LOG_PAGE_LOG_RECORD_END; ++} ++ ++static_assert(offsetof(struct RECORD_PAGE_HDR, file_off) == 0x3c); ++ ++/* ++ * END of NTFS LOG structures ++ */ ++ ++/* Define some tuning parameters to keep the restart tables a reasonable size */ ++#define INITIAL_NUMBER_TRANSACTIONS 5 ++ ++enum NTFS_LOG_OPERATION { ++ ++ Noop = 0x00, ++ CompensationLogRecord = 0x01, ++ InitializeFileRecordSegment = 0x02, ++ DeallocateFileRecordSegment = 0x03, ++ WriteEndOfFileRecordSegment = 0x04, ++ CreateAttribute = 0x05, ++ DeleteAttribute = 0x06, ++ UpdateResidentValue = 0x07, ++ UpdateNonresidentValue = 0x08, ++ UpdateMappingPairs = 0x09, ++ DeleteDirtyClusters = 0x0A, ++ SetNewAttributeSizes = 0x0B, ++ AddIndexEntryRoot = 0x0C, ++ DeleteIndexEntryRoot = 0x0D, ++ AddIndexEntryAllocation = 0x0E, ++ DeleteIndexEntryAllocation = 0x0F, ++ WriteEndOfIndexBuffer = 0x10, ++ SetIndexEntryVcnRoot = 0x11, ++ SetIndexEntryVcnAllocation = 0x12, ++ UpdateFileNameRoot = 0x13, ++ UpdateFileNameAllocation = 0x14, ++ SetBitsInNonresidentBitMap = 0x15, ++ ClearBitsInNonresidentBitMap = 0x16, ++ HotFix = 0x17, ++ EndTopLevelAction = 0x18, ++ PrepareTransaction = 0x19, ++ CommitTransaction = 0x1A, ++ ForgetTransaction = 0x1B, ++ OpenNonresidentAttribute = 0x1C, ++ OpenAttributeTableDump = 0x1D, ++ AttributeNamesDump = 0x1E, ++ DirtyPageTableDump = 0x1F, ++ TransactionTableDump = 0x20, ++ UpdateRecordDataRoot = 0x21, ++ UpdateRecordDataAllocation = 0x22, ++ ++ UpdateRelativeDataInIndex = ++ 0x23, // NtOfsRestartUpdateRelativeDataInIndex ++ UpdateRelativeDataInIndex2 = 0x24, ++ ZeroEndOfFileRecord = 0x25, ++}; ++ ++/* ++ * Array for log records which require a target attribute ++ * A true indicates that the corresponding restart operation requires a target attribute ++ */ ++static const u8 AttributeRequired[] = { ++ 0xFC, 0xFB, 0xFF, 0x10, 0x06, ++}; ++ ++static inline bool is_target_required(u16 op) ++{ ++ bool ret = op <= UpdateRecordDataAllocation && ++ (AttributeRequired[op >> 3] >> (op & 7) & 1); ++ return ret; ++} ++ ++static inline bool can_skip_action(enum NTFS_LOG_OPERATION op) ++{ ++ switch (op) { ++ case Noop: ++ case DeleteDirtyClusters: ++ case HotFix: ++ case EndTopLevelAction: ++ case PrepareTransaction: ++ case CommitTransaction: ++ case ForgetTransaction: ++ case CompensationLogRecord: ++ case OpenNonresidentAttribute: ++ case OpenAttributeTableDump: ++ case AttributeNamesDump: ++ case DirtyPageTableDump: ++ case TransactionTableDump: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++enum { lcb_ctx_undo_next, lcb_ctx_prev, lcb_ctx_next }; ++ ++/* bytes per restart table */ ++static inline u32 bytes_per_rt(const struct RESTART_TABLE *rt) ++{ ++ return le16_to_cpu(rt->used) * le16_to_cpu(rt->size) + ++ sizeof(struct RESTART_TABLE); ++} ++ ++/* log record length */ ++static inline u32 lrh_length(const struct LOG_REC_HDR *lr) ++{ ++ u16 t16 = le16_to_cpu(lr->lcns_follow); ++ ++ return struct_size(lr, page_lcns, max_t(u16, 1, t16)); ++} ++ ++struct lcb { ++ struct LFS_RECORD_HDR *lrh; // Log record header of the current lsn ++ struct LOG_REC_HDR *log_rec; ++ u32 ctx_mode; // lcb_ctx_undo_next/lcb_ctx_prev/lcb_ctx_next ++ struct CLIENT_ID client; ++ bool alloc; // if true the we should deallocate 'log_rec' ++}; ++ ++static void lcb_put(struct lcb *lcb) ++{ ++ if (lcb->alloc) ++ ntfs_free(lcb->log_rec); ++ ntfs_free(lcb->lrh); ++ ntfs_free(lcb); ++} ++ ++/* ++ * oldest_client_lsn ++ * ++ * find the oldest lsn from active clients. ++ */ ++static inline void oldest_client_lsn(const struct CLIENT_REC *ca, ++ __le16 next_client, u64 *oldest_lsn) ++{ ++ while (next_client != LFS_NO_CLIENT_LE) { ++ const struct CLIENT_REC *cr = ca + le16_to_cpu(next_client); ++ u64 lsn = le64_to_cpu(cr->oldest_lsn); ++ ++ /* ignore this block if it's oldest lsn is 0 */ ++ if (lsn && lsn < *oldest_lsn) ++ *oldest_lsn = lsn; ++ ++ next_client = cr->next_client; ++ } ++} ++ ++static inline bool is_rst_page_hdr_valid(u32 file_off, ++ const struct RESTART_HDR *rhdr) ++{ ++ u32 sys_page = le32_to_cpu(rhdr->sys_page_size); ++ u32 page_size = le32_to_cpu(rhdr->page_size); ++ u32 end_usa; ++ u16 ro; ++ ++ if (sys_page < SECTOR_SIZE || page_size < SECTOR_SIZE || ++ sys_page & (sys_page - 1) || page_size & (page_size - 1)) { ++ return false; ++ } ++ ++ /* Check that if the file offset isn't 0, it is the system page size */ ++ if (file_off && file_off != sys_page) ++ return false; ++ ++ /* Check support version 1.1+ */ ++ if (le16_to_cpu(rhdr->major_ver) <= 1 && !rhdr->minor_ver) ++ return false; ++ ++ if (le16_to_cpu(rhdr->major_ver) > 2) ++ return false; ++ ++ ro = le16_to_cpu(rhdr->ra_off); ++ if (!IsQuadAligned(ro) || ro > sys_page) ++ return false; ++ ++ end_usa = ((sys_page >> SECTOR_SHIFT) + 1) * sizeof(short); ++ end_usa += le16_to_cpu(rhdr->rhdr.fix_off); ++ ++ if (ro < end_usa) ++ return false; ++ ++ return true; ++} ++ ++static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) ++{ ++ const struct RESTART_AREA *ra; ++ u16 cl, fl, ul; ++ u32 off, l_size, file_dat_bits, file_size_round; ++ u16 ro = le16_to_cpu(rhdr->ra_off); ++ u32 sys_page = le32_to_cpu(rhdr->sys_page_size); ++ ++ if (ro + offsetof(struct RESTART_AREA, l_size) > ++ SECTOR_SIZE - sizeof(short)) ++ return false; ++ ++ ra = Add2Ptr(rhdr, ro); ++ cl = le16_to_cpu(ra->log_clients); ++ ++ if (cl > 1) ++ return false; ++ ++ off = le16_to_cpu(ra->client_off); ++ ++ if (!IsQuadAligned(off) || ro + off > SECTOR_SIZE - sizeof(short)) ++ return false; ++ ++ off += cl * sizeof(struct CLIENT_REC); ++ ++ if (off > sys_page) ++ return false; ++ ++ /* ++ * Check the restart length field and whether the entire ++ * restart area is contained that length ++ */ ++ if (le16_to_cpu(rhdr->ra_off) + le16_to_cpu(ra->ra_len) > sys_page || ++ off > le16_to_cpu(ra->ra_len)) { ++ return false; ++ } ++ ++ /* ++ * As a final check make sure that the use list and the free list ++ * are either empty or point to a valid client ++ */ ++ fl = le16_to_cpu(ra->client_idx[0]); ++ ul = le16_to_cpu(ra->client_idx[1]); ++ if ((fl != LFS_NO_CLIENT && fl >= cl) || ++ (ul != LFS_NO_CLIENT && ul >= cl)) ++ return false; ++ ++ /* Make sure the sequence number bits match the log file size */ ++ l_size = le64_to_cpu(ra->l_size); ++ ++ file_dat_bits = sizeof(u64) * 8 - le32_to_cpu(ra->seq_num_bits); ++ file_size_round = 1u << (file_dat_bits + 3); ++ if (file_size_round != l_size && ++ (file_size_round < l_size || (file_size_round / 2) > l_size)) { ++ return false; ++ } ++ ++ /* The log page data offset and record header length must be quad-aligned */ ++ if (!IsQuadAligned(le16_to_cpu(ra->data_off)) || ++ !IsQuadAligned(le16_to_cpu(ra->rec_hdr_len))) ++ return false; ++ ++ return true; ++} ++ ++static inline bool is_client_area_valid(const struct RESTART_HDR *rhdr, ++ bool usa_error) ++{ ++ u16 ro = le16_to_cpu(rhdr->ra_off); ++ const struct RESTART_AREA *ra = Add2Ptr(rhdr, ro); ++ u16 ra_len = le16_to_cpu(ra->ra_len); ++ const struct CLIENT_REC *ca; ++ u32 i; ++ ++ if (usa_error && ra_len + ro > SECTOR_SIZE - sizeof(short)) ++ return false; ++ ++ /* Find the start of the client array */ ++ ca = Add2Ptr(ra, le16_to_cpu(ra->client_off)); ++ ++ /* ++ * Start with the free list ++ * Check that all the clients are valid and that there isn't a cycle ++ * Do the in-use list on the second pass ++ */ ++ for (i = 0; i < 2; i++) { ++ u16 client_idx = le16_to_cpu(ra->client_idx[i]); ++ bool first_client = true; ++ u16 clients = le16_to_cpu(ra->log_clients); ++ ++ while (client_idx != LFS_NO_CLIENT) { ++ const struct CLIENT_REC *cr; ++ ++ if (!clients || ++ client_idx >= le16_to_cpu(ra->log_clients)) ++ return false; ++ ++ clients -= 1; ++ cr = ca + client_idx; ++ ++ client_idx = le16_to_cpu(cr->next_client); ++ ++ if (first_client) { ++ first_client = false; ++ if (cr->prev_client != LFS_NO_CLIENT_LE) ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * remove_client ++ * ++ * remove a client record from a client record list an restart area ++ */ ++static inline void remove_client(struct CLIENT_REC *ca, ++ const struct CLIENT_REC *cr, __le16 *head) ++{ ++ if (cr->prev_client == LFS_NO_CLIENT_LE) ++ *head = cr->next_client; ++ else ++ ca[le16_to_cpu(cr->prev_client)].next_client = cr->next_client; ++ ++ if (cr->next_client != LFS_NO_CLIENT_LE) ++ ca[le16_to_cpu(cr->next_client)].prev_client = cr->prev_client; ++} ++ ++/* ++ * add_client ++ * ++ * add a client record to the start of a list ++ */ ++static inline void add_client(struct CLIENT_REC *ca, u16 index, __le16 *head) ++{ ++ struct CLIENT_REC *cr = ca + index; ++ ++ cr->prev_client = LFS_NO_CLIENT_LE; ++ cr->next_client = *head; ++ ++ if (*head != LFS_NO_CLIENT_LE) ++ ca[le16_to_cpu(*head)].prev_client = cpu_to_le16(index); ++ ++ *head = cpu_to_le16(index); ++} ++ ++/* ++ * enum_rstbl ++ * ++ */ ++static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) ++{ ++ __le32 *e; ++ u32 bprt; ++ u16 rsize = t ? le16_to_cpu(t->size) : 0; ++ ++ if (!c) { ++ if (!t || !t->total) ++ return NULL; ++ e = Add2Ptr(t, sizeof(struct RESTART_TABLE)); ++ } else { ++ e = Add2Ptr(c, rsize); ++ } ++ ++ /* Loop until we hit the first one allocated, or the end of the list */ ++ for (bprt = bytes_per_rt(t); PtrOffset(t, e) < bprt; ++ e = Add2Ptr(e, rsize)) { ++ if (*e == RESTART_ENTRY_ALLOCATED_LE) ++ return e; ++ } ++ return NULL; ++} ++ ++/* ++ * find_dp ++ * ++ * searches for a 'vcn' in Dirty Page Table, ++ */ ++static inline struct DIR_PAGE_ENTRY *find_dp(struct RESTART_TABLE *dptbl, ++ u32 target_attr, u64 vcn) ++{ ++ __le32 ta = cpu_to_le32(target_attr); ++ struct DIR_PAGE_ENTRY *dp = NULL; ++ ++ while ((dp = enum_rstbl(dptbl, dp))) { ++ u64 dp_vcn = le64_to_cpu(dp->vcn); ++ ++ if (dp->target_attr == ta && vcn >= dp_vcn && ++ vcn < dp_vcn + le32_to_cpu(dp->lcns_follow)) { ++ return dp; ++ } ++ } ++ return NULL; ++} ++ ++static inline u32 norm_file_page(u32 page_size, u32 *l_size, bool use_default) ++{ ++ if (use_default) ++ page_size = DefaultLogPageSize; ++ ++ /* Round the file size down to a system page boundary */ ++ *l_size &= ~(page_size - 1); ++ ++ /* File should contain at least 2 restart pages and MinLogRecordPages pages */ ++ if (*l_size < (MinLogRecordPages + 2) * page_size) ++ return 0; ++ ++ return page_size; ++} ++ ++static bool check_log_rec(const struct LOG_REC_HDR *lr, u32 bytes, u32 tr, ++ u32 bytes_per_attr_entry) ++{ ++ u16 t16; ++ ++ if (bytes < sizeof(struct LOG_REC_HDR)) ++ return false; ++ if (!tr) ++ return false; ++ ++ if ((tr - sizeof(struct RESTART_TABLE)) % ++ sizeof(struct TRANSACTION_ENTRY)) ++ return false; ++ ++ if (le16_to_cpu(lr->redo_off) & 7) ++ return false; ++ ++ if (le16_to_cpu(lr->undo_off) & 7) ++ return false; ++ ++ if (lr->target_attr) ++ goto check_lcns; ++ ++ if (is_target_required(le16_to_cpu(lr->redo_op))) ++ return false; ++ ++ if (is_target_required(le16_to_cpu(lr->undo_op))) ++ return false; ++ ++check_lcns: ++ if (!lr->lcns_follow) ++ goto check_length; ++ ++ t16 = le16_to_cpu(lr->target_attr); ++ if ((t16 - sizeof(struct RESTART_TABLE)) % bytes_per_attr_entry) ++ return false; ++ ++check_length: ++ if (bytes < lrh_length(lr)) ++ return false; ++ ++ return true; ++} ++ ++static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) ++{ ++ u32 ts; ++ u32 i, off; ++ u16 rsize = le16_to_cpu(rt->size); ++ u16 ne = le16_to_cpu(rt->used); ++ u32 ff = le32_to_cpu(rt->first_free); ++ u32 lf = le32_to_cpu(rt->last_free); ++ ++ ts = rsize * ne + sizeof(struct RESTART_TABLE); ++ ++ if (!rsize || rsize > bytes || ++ rsize + sizeof(struct RESTART_TABLE) > bytes || bytes < ts || ++ le16_to_cpu(rt->total) > ne || ff > ts || lf > ts || ++ (ff && ff < sizeof(struct RESTART_TABLE)) || ++ (lf && lf < sizeof(struct RESTART_TABLE))) { ++ return false; ++ } ++ ++ /* Verify each entry is either allocated or points ++ * to a valid offset the table ++ */ ++ for (i = 0; i < ne; i++) { ++ off = le32_to_cpu(*(__le32 *)Add2Ptr( ++ rt, i * rsize + sizeof(struct RESTART_TABLE))); ++ ++ if (off != RESTART_ENTRY_ALLOCATED && off && ++ (off < sizeof(struct RESTART_TABLE) || ++ ((off - sizeof(struct RESTART_TABLE)) % rsize))) { ++ return false; ++ } ++ } ++ ++ /* Walk through the list headed by the first entry to make ++ * sure none of the entries are currently being used ++ */ ++ for (off = ff; off;) { ++ if (off == RESTART_ENTRY_ALLOCATED) ++ return false; ++ ++ off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off)); ++ } ++ ++ return true; ++} ++ ++/* ++ * free_rsttbl_idx ++ * ++ * frees a previously allocated index a Restart Table. ++ */ ++static inline void free_rsttbl_idx(struct RESTART_TABLE *rt, u32 off) ++{ ++ __le32 *e; ++ u32 lf = le32_to_cpu(rt->last_free); ++ __le32 off_le = cpu_to_le32(off); ++ ++ e = Add2Ptr(rt, off); ++ ++ if (off < le32_to_cpu(rt->free_goal)) { ++ *e = rt->first_free; ++ rt->first_free = off_le; ++ if (!lf) ++ rt->last_free = off_le; ++ } else { ++ if (lf) ++ *(__le32 *)Add2Ptr(rt, lf) = off_le; ++ else ++ rt->first_free = off_le; ++ ++ rt->last_free = off_le; ++ *e = 0; ++ } ++ ++ le16_sub_cpu(&rt->total, 1); ++} ++ ++static inline struct RESTART_TABLE *init_rsttbl(u16 esize, u16 used) ++{ ++ __le32 *e, *last_free; ++ u32 off; ++ u32 bytes = esize * used + sizeof(struct RESTART_TABLE); ++ u32 lf = sizeof(struct RESTART_TABLE) + (used - 1) * esize; ++ struct RESTART_TABLE *t = ntfs_zalloc(bytes); ++ ++ t->size = cpu_to_le16(esize); ++ t->used = cpu_to_le16(used); ++ t->free_goal = cpu_to_le32(~0u); ++ t->first_free = cpu_to_le32(sizeof(struct RESTART_TABLE)); ++ t->last_free = cpu_to_le32(lf); ++ ++ e = (__le32 *)(t + 1); ++ last_free = Add2Ptr(t, lf); ++ ++ for (off = sizeof(struct RESTART_TABLE) + esize; e < last_free; ++ e = Add2Ptr(e, esize), off += esize) { ++ *e = cpu_to_le32(off); ++ } ++ return t; ++} ++ ++static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl, ++ u32 add, u32 free_goal) ++{ ++ u16 esize = le16_to_cpu(tbl->size); ++ __le32 osize = cpu_to_le32(bytes_per_rt(tbl)); ++ u32 used = le16_to_cpu(tbl->used); ++ struct RESTART_TABLE *rt = init_rsttbl(esize, used + add); ++ ++ memcpy(rt + 1, tbl + 1, esize * used); ++ ++ rt->free_goal = free_goal == ~0u ++ ? cpu_to_le32(~0u) ++ : cpu_to_le32(sizeof(struct RESTART_TABLE) + ++ free_goal * esize); ++ ++ if (tbl->first_free) { ++ rt->first_free = tbl->first_free; ++ *(__le32 *)Add2Ptr(rt, le32_to_cpu(tbl->last_free)) = osize; ++ } else { ++ rt->first_free = osize; ++ } ++ ++ rt->total = tbl->total; ++ ++ ntfs_free(tbl); ++ return rt; ++} ++ ++/* ++ * alloc_rsttbl_idx ++ * ++ * allocates an index from within a previously initialized Restart Table ++ */ ++static inline void *alloc_rsttbl_idx(struct RESTART_TABLE **tbl) ++{ ++ u32 off; ++ __le32 *e; ++ struct RESTART_TABLE *t = *tbl; ++ ++ if (!t->first_free) ++ *tbl = t = extend_rsttbl(t, 16, ~0u); ++ ++ off = le32_to_cpu(t->first_free); ++ ++ /* Dequeue this entry and zero it. */ ++ e = Add2Ptr(t, off); ++ ++ t->first_free = *e; ++ ++ memset(e, 0, le16_to_cpu(t->size)); ++ ++ *e = RESTART_ENTRY_ALLOCATED_LE; ++ ++ /* If list is going empty, then we fix the last_free as well. */ ++ if (!t->first_free) ++ t->last_free = 0; ++ ++ le16_add_cpu(&t->total, 1); ++ ++ return Add2Ptr(t, off); ++} ++ ++/* ++ * alloc_rsttbl_from_idx ++ * ++ * allocates a specific index from within a previously initialized Restart Table ++ */ ++static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) ++{ ++ u32 off; ++ __le32 *e; ++ struct RESTART_TABLE *rt = *tbl; ++ u32 bytes = bytes_per_rt(rt); ++ u16 esize = le16_to_cpu(rt->size); ++ ++ /* If the entry is not the table, we will have to extend the table */ ++ if (vbo >= bytes) { ++ /* ++ * extend the size by computing the number of entries between ++ * the existing size and the desired index and adding ++ * 1 to that ++ */ ++ u32 bytes2idx = vbo - bytes; ++ ++ /* There should always be an integral number of entries being added */ ++ /* Now extend the table */ ++ *tbl = rt = extend_rsttbl(rt, bytes2idx / esize + 1, bytes); ++ if (!rt) ++ return NULL; ++ } ++ ++ /* see if the entry is already allocated, and just return if it is. */ ++ e = Add2Ptr(rt, vbo); ++ ++ if (*e == RESTART_ENTRY_ALLOCATED_LE) ++ return e; ++ ++ /* ++ * Walk through the table, looking for the entry we're ++ * interested and the previous entry ++ */ ++ off = le32_to_cpu(rt->first_free); ++ e = Add2Ptr(rt, off); ++ ++ if (off == vbo) { ++ /* this is a match */ ++ rt->first_free = *e; ++ goto skip_looking; ++ } ++ ++ /* ++ * need to walk through the list looking for the predecessor of our entry ++ */ ++ for (;;) { ++ /* Remember the entry just found */ ++ u32 last_off = off; ++ __le32 *last_e = e; ++ ++ /* should never run of entries. */ ++ ++ /* Lookup up the next entry the list */ ++ off = le32_to_cpu(*last_e); ++ e = Add2Ptr(rt, off); ++ ++ /* If this is our match we are done */ ++ if (off == vbo) { ++ *last_e = *e; ++ ++ /* If this was the last entry, we update that the table as well */ ++ if (le32_to_cpu(rt->last_free) == off) ++ rt->last_free = cpu_to_le32(last_off); ++ break; ++ } ++ } ++ ++skip_looking: ++ /* If the list is now empty, we fix the last_free as well */ ++ if (!rt->first_free) ++ rt->last_free = 0; ++ ++ /* Zero this entry */ ++ memset(e, 0, esize); ++ *e = RESTART_ENTRY_ALLOCATED_LE; ++ ++ le16_add_cpu(&rt->total, 1); ++ ++ return e; ++} ++ ++#define RESTART_SINGLE_PAGE_IO cpu_to_le16(0x0001) ++ ++#define NTFSLOG_WRAPPED 0x00000001 ++#define NTFSLOG_MULTIPLE_PAGE_IO 0x00000002 ++#define NTFSLOG_NO_LAST_LSN 0x00000004 ++#define NTFSLOG_REUSE_TAIL 0x00000010 ++#define NTFSLOG_NO_OLDEST_LSN 0x00000020 ++ ++/* ++ * Helper struct to work with NTFS LogFile ++ */ ++struct ntfs_log { ++ struct ntfs_inode *ni; ++ ++ u32 l_size; ++ u32 sys_page_size; ++ u32 sys_page_mask; ++ u32 page_size; ++ u32 page_mask; // page_size - 1 ++ u8 page_bits; ++ struct RECORD_PAGE_HDR *one_page_buf; ++ ++ struct RESTART_TABLE *open_attr_tbl; ++ u32 transaction_id; ++ u32 clst_per_page; ++ ++ u32 first_page; ++ u32 next_page; ++ u32 ra_off; ++ u32 data_off; ++ u32 restart_size; ++ u32 data_size; ++ u16 record_header_len; ++ u64 seq_num; ++ u32 seq_num_bits; ++ u32 file_data_bits; ++ u32 seq_num_mask; /* (1 << file_data_bits) - 1 */ ++ ++ struct RESTART_AREA *ra; /* in-memory image of the next restart area */ ++ u32 ra_size; /* the usable size of the restart area */ ++ ++ /* ++ * If true, then the in-memory restart area is to be written ++ * to the first position on the disk ++ */ ++ bool init_ra; ++ bool set_dirty; /* true if we need to set dirty flag */ ++ ++ u64 oldest_lsn; ++ ++ u32 oldest_lsn_off; ++ u64 last_lsn; ++ ++ u32 total_avail; ++ u32 total_avail_pages; ++ u32 total_undo_commit; ++ u32 max_current_avail; ++ u32 current_avail; ++ u32 reserved; ++ ++ short major_ver; ++ short minor_ver; ++ ++ u32 l_flags; /* See NTFSLOG_XXX */ ++ u32 current_openlog_count; /* On-disk value for open_log_count */ ++ ++ struct CLIENT_ID client_id; ++ u32 client_undo_commit; ++}; ++ ++static inline u32 lsn_to_vbo(struct ntfs_log *log, const u64 lsn) ++{ ++ u32 vbo = (lsn << log->seq_num_bits) >> (log->seq_num_bits - 3); ++ ++ return vbo; ++} ++ ++/* compute the offset in the log file of the next log page */ ++static inline u32 next_page_off(struct ntfs_log *log, u32 off) ++{ ++ off = (off & ~log->sys_page_mask) + log->page_size; ++ return off >= log->l_size ? log->first_page : off; ++} ++ ++static inline u32 lsn_to_page_off(struct ntfs_log *log, u64 lsn) ++{ ++ return (((u32)lsn) << 3) & log->page_mask; ++} ++ ++static inline u64 vbo_to_lsn(struct ntfs_log *log, u32 off, u64 Seq) ++{ ++ return (off >> 3) + (Seq << log->file_data_bits); ++} ++ ++static inline bool is_lsn_in_file(struct ntfs_log *log, u64 lsn) ++{ ++ return lsn >= log->oldest_lsn && ++ lsn <= le64_to_cpu(log->ra->current_lsn); ++} ++ ++static inline u32 hdr_file_off(struct ntfs_log *log, ++ struct RECORD_PAGE_HDR *hdr) ++{ ++ if (log->major_ver < 2) ++ return le64_to_cpu(hdr->rhdr.lsn); ++ ++ return le32_to_cpu(hdr->file_off); ++} ++ ++static inline u64 base_lsn(struct ntfs_log *log, ++ const struct RECORD_PAGE_HDR *hdr, u64 lsn) ++{ ++ u64 h_lsn = le64_to_cpu(hdr->rhdr.lsn); ++ u64 ret = (((h_lsn >> log->file_data_bits) + ++ (lsn < (lsn_to_vbo(log, h_lsn) & ~log->page_mask) ? 1 : 0)) ++ << log->file_data_bits) + ++ ((((is_log_record_end(hdr) && ++ h_lsn <= le64_to_cpu(hdr->record_hdr.last_end_lsn)) ++ ? le16_to_cpu(hdr->record_hdr.next_record_off) ++ : log->page_size) + ++ lsn) >> ++ 3); ++ ++ return ret; ++} ++ ++static inline bool verify_client_lsn(struct ntfs_log *log, ++ const struct CLIENT_REC *client, u64 lsn) ++{ ++ return lsn >= le64_to_cpu(client->oldest_lsn) && ++ lsn <= le64_to_cpu(log->ra->current_lsn) && lsn; ++} ++ ++struct restart_info { ++ u64 last_lsn; ++ struct RESTART_HDR *r_page; ++ u32 vbo; ++ bool chkdsk_was_run; ++ bool valid_page; ++ bool initialized; ++ bool restart; ++}; ++ ++static int read_log_page(struct ntfs_log *log, u32 vbo, ++ struct RECORD_PAGE_HDR **buffer, bool *usa_error) ++{ ++ int err = 0; ++ u32 page_idx = vbo >> log->page_bits; ++ u32 page_off = vbo & log->page_mask; ++ u32 bytes = log->page_size - page_off; ++ void *to_free = NULL; ++ u32 page_vbo = page_idx << log->page_bits; ++ struct RECORD_PAGE_HDR *page_buf; ++ struct ntfs_inode *ni = log->ni; ++ bool bBAAD; ++ ++ if (vbo >= log->l_size) ++ return -EINVAL; ++ ++ if (!*buffer) { ++ to_free = ntfs_malloc(bytes); ++ if (!to_free) ++ return -ENOMEM; ++ *buffer = to_free; ++ } ++ ++ page_buf = page_off ? log->one_page_buf : *buffer; ++ ++ err = ntfs_read_run_nb(ni->mi.sbi, &ni->file.run, page_vbo, page_buf, ++ log->page_size, NULL); ++ if (err) ++ goto out; ++ ++ if (page_buf->rhdr.sign != NTFS_FFFF_SIGNATURE) ++ ntfs_fix_post_read(&page_buf->rhdr, PAGE_SIZE, false); ++ ++ if (page_buf != *buffer) ++ memcpy(*buffer, Add2Ptr(page_buf, page_off), bytes); ++ ++ bBAAD = page_buf->rhdr.sign == NTFS_BAAD_SIGNATURE; ++ ++ if (usa_error) ++ *usa_error = bBAAD; ++ /* Check that the update sequence array for this page is valid */ ++ /* If we don't allow errors, raise an error status */ ++ else if (bBAAD) ++ err = -EINVAL; ++ ++out: ++ if (err && to_free) { ++ ntfs_free(to_free); ++ *buffer = NULL; ++ } ++ ++ return err; ++} ++ ++/* ++ * log_read_rst ++ * ++ * it walks through 512 blocks of the file looking for a valid restart page header ++ * It will stop the first time we find a valid page header ++ */ ++static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, ++ struct restart_info *info) ++{ ++ u32 skip, vbo; ++ struct RESTART_HDR *r_page = ntfs_malloc(DefaultLogPageSize); ++ ++ if (!r_page) ++ return -ENOMEM; ++ ++ memset(info, 0, sizeof(struct restart_info)); ++ ++ /* Determine which restart area we are looking for */ ++ if (first) { ++ vbo = 0; ++ skip = 512; ++ } else { ++ vbo = 512; ++ skip = 0; ++ } ++ ++ /* loop continuously until we succeed */ ++ for (; vbo < l_size; vbo = 2 * vbo + skip, skip = 0) { ++ bool usa_error; ++ u32 sys_page_size; ++ bool brst, bchk; ++ struct RESTART_AREA *ra; ++ ++ /* Read a page header at the current offset */ ++ if (read_log_page(log, vbo, (struct RECORD_PAGE_HDR **)&r_page, ++ &usa_error)) { ++ /* ignore any errors */ ++ continue; ++ } ++ ++ /* exit if the signature is a log record page */ ++ if (r_page->rhdr.sign == NTFS_RCRD_SIGNATURE) { ++ info->initialized = true; ++ break; ++ } ++ ++ brst = r_page->rhdr.sign == NTFS_RSTR_SIGNATURE; ++ bchk = r_page->rhdr.sign == NTFS_CHKD_SIGNATURE; ++ ++ if (!bchk && !brst) { ++ if (r_page->rhdr.sign != NTFS_FFFF_SIGNATURE) { ++ /* ++ * Remember if the signature does not ++ * indicate uninitialized file ++ */ ++ info->initialized = true; ++ } ++ continue; ++ } ++ ++ ra = NULL; ++ info->valid_page = false; ++ info->initialized = true; ++ info->vbo = vbo; ++ ++ /* Let's check the restart area if this is a valid page */ ++ if (!is_rst_page_hdr_valid(vbo, r_page)) ++ goto check_result; ++ ra = Add2Ptr(r_page, le16_to_cpu(r_page->ra_off)); ++ ++ if (!is_rst_area_valid(r_page)) ++ goto check_result; ++ ++ /* ++ * We have a valid restart page header and restart area. ++ * If chkdsk was run or we have no clients then we have ++ * no more checking to do ++ */ ++ if (bchk || ra->client_idx[1] == LFS_NO_CLIENT_LE) { ++ info->valid_page = true; ++ goto check_result; ++ } ++ ++ /* Read the entire restart area */ ++ sys_page_size = le32_to_cpu(r_page->sys_page_size); ++ if (DefaultLogPageSize != sys_page_size) { ++ ntfs_free(r_page); ++ r_page = ntfs_zalloc(sys_page_size); ++ if (!r_page) ++ return -ENOMEM; ++ ++ if (read_log_page(log, vbo, ++ (struct RECORD_PAGE_HDR **)&r_page, ++ &usa_error)) { ++ /* ignore any errors */ ++ ntfs_free(r_page); ++ r_page = NULL; ++ continue; ++ } ++ } ++ ++ if (is_client_area_valid(r_page, usa_error)) { ++ info->valid_page = true; ++ ra = Add2Ptr(r_page, le16_to_cpu(r_page->ra_off)); ++ } ++ ++check_result: ++ /* If chkdsk was run then update the caller's values and return */ ++ if (r_page->rhdr.sign == NTFS_CHKD_SIGNATURE) { ++ info->chkdsk_was_run = true; ++ info->last_lsn = le64_to_cpu(r_page->rhdr.lsn); ++ info->restart = true; ++ info->r_page = r_page; ++ return 0; ++ } ++ ++ /* If we have a valid page then copy the values we need from it */ ++ if (info->valid_page) { ++ info->last_lsn = le64_to_cpu(ra->current_lsn); ++ info->restart = true; ++ info->r_page = r_page; ++ return 0; ++ } ++ } ++ ++ ntfs_free(r_page); ++ ++ return 0; ++} ++ ++/* ++ * log_init_pg_hdr ++ * ++ * init "log' from restart page header ++ */ ++static void log_init_pg_hdr(struct ntfs_log *log, u32 sys_page_size, ++ u32 page_size, u16 major_ver, u16 minor_ver) ++{ ++ log->sys_page_size = sys_page_size; ++ log->sys_page_mask = sys_page_size - 1; ++ log->page_size = page_size; ++ log->page_mask = page_size - 1; ++ log->page_bits = blksize_bits(page_size); ++ ++ log->clst_per_page = log->page_size >> log->ni->mi.sbi->cluster_bits; ++ if (!log->clst_per_page) ++ log->clst_per_page = 1; ++ ++ log->first_page = major_ver >= 2 ++ ? 0x22 * page_size ++ : ((sys_page_size << 1) + (page_size << 1)); ++ log->major_ver = major_ver; ++ log->minor_ver = minor_ver; ++} ++ ++/* ++ * log_create ++ * ++ * init "log" in cases when we don't have a restart area to use ++ */ ++static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, ++ u32 open_log_count, bool wrapped, bool use_multi_page) ++{ ++ log->l_size = l_size; ++ /* All file offsets must be quadword aligned */ ++ log->file_data_bits = blksize_bits(l_size) - 3; ++ log->seq_num_mask = (8 << log->file_data_bits) - 1; ++ log->seq_num_bits = sizeof(u64) * 8 - log->file_data_bits; ++ log->seq_num = (last_lsn >> log->file_data_bits) + 2; ++ log->next_page = log->first_page; ++ log->oldest_lsn = log->seq_num << log->file_data_bits; ++ log->oldest_lsn_off = 0; ++ log->last_lsn = log->oldest_lsn; ++ ++ log->l_flags |= NTFSLOG_NO_LAST_LSN | NTFSLOG_NO_OLDEST_LSN; ++ ++ /* Set the correct flags for the I/O and indicate if we have wrapped */ ++ if (wrapped) ++ log->l_flags |= NTFSLOG_WRAPPED; ++ ++ if (use_multi_page) ++ log->l_flags |= NTFSLOG_MULTIPLE_PAGE_IO; ++ ++ /* Compute the log page values */ ++ log->data_off = QuadAlign( ++ offsetof(struct RECORD_PAGE_HDR, fixups) + ++ sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1)); ++ log->data_size = log->page_size - log->data_off; ++ log->record_header_len = sizeof(struct LFS_RECORD_HDR); ++ ++ /* Remember the different page sizes for reservation */ ++ log->reserved = log->data_size - log->record_header_len; ++ ++ /* Compute the restart page values. */ ++ log->ra_off = QuadAlign( ++ offsetof(struct RESTART_HDR, fixups) + ++ sizeof(short) * ((log->sys_page_size >> SECTOR_SHIFT) + 1)); ++ log->restart_size = log->sys_page_size - log->ra_off; ++ log->ra_size = struct_size(log->ra, clients, 1); ++ log->current_openlog_count = open_log_count; ++ ++ /* ++ * The total available log file space is the number of ++ * log file pages times the space available on each page ++ */ ++ log->total_avail_pages = log->l_size - log->first_page; ++ log->total_avail = log->total_avail_pages >> log->page_bits; ++ ++ /* ++ * We assume that we can't use the end of the page less than ++ * the file record size ++ * Then we won't need to reserve more than the caller asks for ++ */ ++ log->max_current_avail = log->total_avail * log->reserved; ++ log->total_avail = log->total_avail * log->data_size; ++ log->current_avail = log->max_current_avail; ++} ++ ++/* ++ * log_create_ra ++ * ++ * This routine is called to fill a restart area from the values stored in 'log' ++ */ ++static struct RESTART_AREA *log_create_ra(struct ntfs_log *log) ++{ ++ struct CLIENT_REC *cr; ++ struct RESTART_AREA *ra = ntfs_zalloc(log->restart_size); ++ ++ if (!ra) ++ return NULL; ++ ++ ra->current_lsn = cpu_to_le64(log->last_lsn); ++ ra->log_clients = cpu_to_le16(1); ++ ra->client_idx[1] = LFS_NO_CLIENT_LE; ++ if (log->l_flags & NTFSLOG_MULTIPLE_PAGE_IO) ++ ra->flags = RESTART_SINGLE_PAGE_IO; ++ ra->seq_num_bits = cpu_to_le32(log->seq_num_bits); ++ ra->ra_len = cpu_to_le16(log->ra_size); ++ ra->client_off = cpu_to_le16(offsetof(struct RESTART_AREA, clients)); ++ ra->l_size = cpu_to_le64(log->l_size); ++ ra->rec_hdr_len = cpu_to_le16(log->record_header_len); ++ ra->data_off = cpu_to_le16(log->data_off); ++ ra->open_log_count = cpu_to_le32(log->current_openlog_count + 1); ++ ++ cr = ra->clients; ++ ++ cr->prev_client = LFS_NO_CLIENT_LE; ++ cr->next_client = LFS_NO_CLIENT_LE; ++ ++ return ra; ++} ++ ++static u32 final_log_off(struct ntfs_log *log, u64 lsn, u32 data_len) ++{ ++ u32 base_vbo = lsn << 3; ++ u32 final_log_off = (base_vbo & log->seq_num_mask) & ~log->page_mask; ++ u32 page_off = base_vbo & log->page_mask; ++ u32 tail = log->page_size - page_off; ++ ++ page_off -= 1; ++ ++ /* Add the length of the header */ ++ data_len += log->record_header_len; ++ ++ /* ++ * If this lsn is contained this log page we are done ++ * Otherwise we need to walk through several log pages ++ */ ++ if (data_len > tail) { ++ data_len -= tail; ++ tail = log->data_size; ++ page_off = log->data_off - 1; ++ ++ for (;;) { ++ final_log_off = next_page_off(log, final_log_off); ++ ++ /* We are done if the remaining bytes fit on this page */ ++ if (data_len <= tail) ++ break; ++ data_len -= tail; ++ } ++ } ++ ++ /* ++ * We add the remaining bytes to our starting position on this page ++ * and then add that value to the file offset of this log page ++ */ ++ return final_log_off + data_len + page_off; ++} ++ ++static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, ++ u64 *lsn) ++{ ++ int err; ++ u64 this_lsn = le64_to_cpu(rh->this_lsn); ++ u32 vbo = lsn_to_vbo(log, this_lsn); ++ u32 end = ++ final_log_off(log, this_lsn, le32_to_cpu(rh->client_data_len)); ++ u32 hdr_off = end & ~log->sys_page_mask; ++ u64 seq = this_lsn >> log->file_data_bits; ++ struct RECORD_PAGE_HDR *page = NULL; ++ ++ /* Remember if we wrapped */ ++ if (end <= vbo) ++ seq += 1; ++ ++ /* log page header for this page */ ++ err = read_log_page(log, hdr_off, &page, NULL); ++ if (err) ++ return err; ++ ++ /* ++ * If the lsn we were given was not the last lsn on this page, ++ * then the starting offset for the next lsn is on a quad word ++ * boundary following the last file offset for the current lsn ++ * Otherwise the file offset is the start of the data on the next page ++ */ ++ if (this_lsn == le64_to_cpu(page->rhdr.lsn)) { ++ /* If we wrapped, we need to increment the sequence number */ ++ hdr_off = next_page_off(log, hdr_off); ++ if (hdr_off == log->first_page) ++ seq += 1; ++ ++ vbo = hdr_off + log->data_off; ++ } else { ++ vbo = QuadAlign(end); ++ } ++ ++ /* Compute the lsn based on the file offset and the sequence count */ ++ *lsn = vbo_to_lsn(log, vbo, seq); ++ ++ /* ++ * If this lsn is within the legal range for the file, we return true ++ * Otherwise false indicates that there are no more lsn's ++ */ ++ if (!is_lsn_in_file(log, *lsn)) ++ *lsn = 0; ++ ++ ntfs_free(page); ++ ++ return 0; ++} ++ ++/* ++ * current_log_avail ++ * ++ * calculate the number of bytes available for log records ++ */ ++static u32 current_log_avail(struct ntfs_log *log) ++{ ++ u32 oldest_off, next_free_off, free_bytes; ++ ++ if (log->l_flags & NTFSLOG_NO_LAST_LSN) { ++ /* The entire file is available */ ++ return log->max_current_avail; ++ } ++ ++ /* ++ * If there is a last lsn the restart area then we know that we will ++ * have to compute the free range ++ * If there is no oldest lsn then start at the first page of the file ++ */ ++ oldest_off = (log->l_flags & NTFSLOG_NO_OLDEST_LSN) ++ ? log->first_page ++ : (log->oldest_lsn_off & ~log->sys_page_mask); ++ ++ /* ++ * We will use the next log page offset to compute the next free page\ ++ * If we are going to reuse this page go to the next page ++ * If we are at the first page then use the end of the file ++ */ ++ next_free_off = (log->l_flags & NTFSLOG_REUSE_TAIL) ++ ? log->next_page + log->page_size ++ : log->next_page == log->first_page ++ ? log->l_size ++ : log->next_page; ++ ++ /* If the two offsets are the same then there is no available space */ ++ if (oldest_off == next_free_off) ++ return 0; ++ /* ++ * If the free offset follows the oldest offset then subtract ++ * this range from the total available pages ++ */ ++ free_bytes = ++ oldest_off < next_free_off ++ ? log->total_avail_pages - (next_free_off - oldest_off) ++ : oldest_off - next_free_off; ++ ++ free_bytes >>= log->page_bits; ++ return free_bytes * log->reserved; ++} ++ ++static bool check_subseq_log_page(struct ntfs_log *log, ++ const struct RECORD_PAGE_HDR *rp, u32 vbo, ++ u64 seq) ++{ ++ u64 lsn_seq; ++ const struct NTFS_RECORD_HEADER *rhdr = &rp->rhdr; ++ u64 lsn = le64_to_cpu(rhdr->lsn); ++ ++ if (rhdr->sign == NTFS_FFFF_SIGNATURE || !rhdr->sign) ++ return false; ++ ++ /* ++ * If the last lsn on the page occurs was written after the page ++ * that caused the original error then we have a fatal error ++ */ ++ lsn_seq = lsn >> log->file_data_bits; ++ ++ /* ++ * If the sequence number for the lsn the page is equal or greater ++ * than lsn we expect, then this is a subsequent write ++ */ ++ return lsn_seq >= seq || ++ (lsn_seq == seq - 1 && log->first_page == vbo && ++ vbo != (lsn_to_vbo(log, lsn) & ~log->page_mask)); ++} ++ ++/* ++ * last_log_lsn ++ * ++ * This routine walks through the log pages for a file, searching for the ++ * last log page written to the file ++ */ ++static int last_log_lsn(struct ntfs_log *log) ++{ ++ int err; ++ bool usa_error = false; ++ bool replace_page = false; ++ bool reuse_page = log->l_flags & NTFSLOG_REUSE_TAIL; ++ bool wrapped_file, wrapped; ++ ++ u32 page_cnt = 1, page_pos = 1; ++ u32 page_off = 0, page_off1 = 0, saved_off = 0; ++ u32 final_off, second_off, final_off_prev = 0, second_off_prev = 0; ++ u32 first_file_off = 0, second_file_off = 0; ++ u32 part_io_count = 0; ++ u32 tails = 0; ++ u32 this_off, curpage_off, nextpage_off, remain_pages; ++ ++ u64 expected_seq, seq_base = 0, lsn_base = 0; ++ u64 best_lsn, best_lsn1, best_lsn2; ++ u64 lsn_cur, lsn1, lsn2; ++ u64 last_ok_lsn = reuse_page ? log->last_lsn : 0; ++ ++ u16 cur_pos, best_page_pos; ++ ++ struct RECORD_PAGE_HDR *page = NULL; ++ struct RECORD_PAGE_HDR *tst_page = NULL; ++ struct RECORD_PAGE_HDR *first_tail = NULL; ++ struct RECORD_PAGE_HDR *second_tail = NULL; ++ struct RECORD_PAGE_HDR *tail_page = NULL; ++ struct RECORD_PAGE_HDR *second_tail_prev = NULL; ++ struct RECORD_PAGE_HDR *first_tail_prev = NULL; ++ struct RECORD_PAGE_HDR *page_bufs = NULL; ++ struct RECORD_PAGE_HDR *best_page; ++ ++ if (log->major_ver >= 2) { ++ final_off = 0x02 * log->page_size; ++ second_off = 0x12 * log->page_size; ++ ++ // 0x10 == 0x12 - 0x2 ++ page_bufs = ntfs_malloc(log->page_size * 0x10); ++ if (!page_bufs) ++ return -ENOMEM; ++ } else { ++ second_off = log->first_page - log->page_size; ++ final_off = second_off - log->page_size; ++ } ++ ++next_tail: ++ /* Read second tail page (at pos 3/0x12000) */ ++ if (read_log_page(log, second_off, &second_tail, &usa_error) || ++ usa_error || second_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) { ++ ntfs_free(second_tail); ++ second_tail = NULL; ++ second_file_off = 0; ++ lsn2 = 0; ++ } else { ++ second_file_off = hdr_file_off(log, second_tail); ++ lsn2 = le64_to_cpu(second_tail->record_hdr.last_end_lsn); ++ } ++ ++ /* Read first tail page (at pos 2/0x2000 ) */ ++ if (read_log_page(log, final_off, &first_tail, &usa_error) || ++ usa_error || first_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) { ++ ntfs_free(first_tail); ++ first_tail = NULL; ++ first_file_off = 0; ++ lsn1 = 0; ++ } else { ++ first_file_off = hdr_file_off(log, first_tail); ++ lsn1 = le64_to_cpu(first_tail->record_hdr.last_end_lsn); ++ } ++ ++ if (log->major_ver < 2) { ++ int best_page; ++ ++ first_tail_prev = first_tail; ++ final_off_prev = first_file_off; ++ second_tail_prev = second_tail; ++ second_off_prev = second_file_off; ++ tails = 1; ++ ++ if (!first_tail && !second_tail) ++ goto tail_read; ++ ++ if (first_tail && second_tail) ++ best_page = lsn1 < lsn2 ? 1 : 0; ++ else if (first_tail) ++ best_page = 0; ++ else ++ best_page = 1; ++ ++ page_off = best_page ? second_file_off : first_file_off; ++ seq_base = (best_page ? lsn2 : lsn1) >> log->file_data_bits; ++ goto tail_read; ++ } ++ ++ best_lsn1 = first_tail ? base_lsn(log, first_tail, first_file_off) : 0; ++ best_lsn2 = ++ second_tail ? base_lsn(log, second_tail, second_file_off) : 0; ++ ++ if (first_tail && second_tail) { ++ if (best_lsn1 > best_lsn2) { ++ best_lsn = best_lsn1; ++ best_page = first_tail; ++ this_off = first_file_off; ++ } else { ++ best_lsn = best_lsn2; ++ best_page = second_tail; ++ this_off = second_file_off; ++ } ++ } else if (first_tail) { ++ best_lsn = best_lsn1; ++ best_page = first_tail; ++ this_off = first_file_off; ++ } else if (second_tail) { ++ best_lsn = best_lsn2; ++ best_page = second_tail; ++ this_off = second_file_off; ++ } else { ++ goto tail_read; ++ } ++ ++ best_page_pos = le16_to_cpu(best_page->page_pos); ++ ++ if (!tails) { ++ if (best_page_pos == page_pos) { ++ seq_base = best_lsn >> log->file_data_bits; ++ saved_off = page_off = le32_to_cpu(best_page->file_off); ++ lsn_base = best_lsn; ++ ++ memmove(page_bufs, best_page, log->page_size); ++ ++ page_cnt = le16_to_cpu(best_page->page_count); ++ if (page_cnt > 1) ++ page_pos += 1; ++ ++ tails = 1; ++ } ++ } else if (seq_base == (best_lsn >> log->file_data_bits) && ++ saved_off + log->page_size == this_off && ++ lsn_base < best_lsn && ++ (page_pos != page_cnt || best_page_pos == page_pos || ++ best_page_pos == 1) && ++ (page_pos >= page_cnt || best_page_pos == page_pos)) { ++ u16 bppc = le16_to_cpu(best_page->page_count); ++ ++ saved_off += log->page_size; ++ lsn_base = best_lsn; ++ ++ memmove(Add2Ptr(page_bufs, tails * log->page_size), best_page, ++ log->page_size); ++ ++ tails += 1; ++ ++ if (best_page_pos != bppc) { ++ page_cnt = bppc; ++ page_pos = best_page_pos; ++ ++ if (page_cnt > 1) ++ page_pos += 1; ++ } else { ++ page_pos = page_cnt = 1; ++ } ++ } else { ++ ntfs_free(first_tail); ++ ntfs_free(second_tail); ++ goto tail_read; ++ } ++ ++ ntfs_free(first_tail_prev); ++ first_tail_prev = first_tail; ++ final_off_prev = first_file_off; ++ first_tail = NULL; ++ ++ ntfs_free(second_tail_prev); ++ second_tail_prev = second_tail; ++ second_off_prev = second_file_off; ++ second_tail = NULL; ++ ++ final_off += log->page_size; ++ second_off += log->page_size; ++ ++ if (tails < 0x10) ++ goto next_tail; ++tail_read: ++ first_tail = first_tail_prev; ++ final_off = final_off_prev; ++ ++ second_tail = second_tail_prev; ++ second_off = second_off_prev; ++ ++ page_cnt = page_pos = 1; ++ ++ curpage_off = seq_base == log->seq_num ? min(log->next_page, page_off) ++ : log->next_page; ++ ++ wrapped_file = ++ curpage_off == log->first_page && ++ !(log->l_flags & (NTFSLOG_NO_LAST_LSN | NTFSLOG_REUSE_TAIL)); ++ ++ expected_seq = wrapped_file ? (log->seq_num + 1) : log->seq_num; ++ ++ nextpage_off = curpage_off; ++ ++next_page: ++ tail_page = NULL; ++ /* Read the next log page */ ++ err = read_log_page(log, curpage_off, &page, &usa_error); ++ ++ /* Compute the next log page offset the file */ ++ nextpage_off = next_page_off(log, curpage_off); ++ wrapped = nextpage_off == log->first_page; ++ ++ if (tails > 1) { ++ struct RECORD_PAGE_HDR *cur_page = ++ Add2Ptr(page_bufs, curpage_off - page_off); ++ ++ if (curpage_off == saved_off) { ++ tail_page = cur_page; ++ goto use_tail_page; ++ } ++ ++ if (page_off > curpage_off || curpage_off >= saved_off) ++ goto use_tail_page; ++ ++ if (page_off1) ++ goto use_cur_page; ++ ++ if (!err && !usa_error && ++ page->rhdr.sign == NTFS_RCRD_SIGNATURE && ++ cur_page->rhdr.lsn == page->rhdr.lsn && ++ cur_page->record_hdr.next_record_off == ++ page->record_hdr.next_record_off && ++ ((page_pos == page_cnt && ++ le16_to_cpu(page->page_pos) == 1) || ++ (page_pos != page_cnt && ++ le16_to_cpu(page->page_pos) == page_pos + 1 && ++ le16_to_cpu(page->page_count) == page_cnt))) { ++ cur_page = NULL; ++ goto use_tail_page; ++ } ++ ++ page_off1 = page_off; ++ ++use_cur_page: ++ ++ lsn_cur = le64_to_cpu(cur_page->rhdr.lsn); ++ ++ if (last_ok_lsn != ++ le64_to_cpu(cur_page->record_hdr.last_end_lsn) && ++ ((lsn_cur >> log->file_data_bits) + ++ ((curpage_off < ++ (lsn_to_vbo(log, lsn_cur) & ~log->page_mask)) ++ ? 1 ++ : 0)) != expected_seq) { ++ goto check_tail; ++ } ++ ++ if (!is_log_record_end(cur_page)) { ++ tail_page = NULL; ++ last_ok_lsn = lsn_cur; ++ goto next_page_1; ++ } ++ ++ log->seq_num = expected_seq; ++ log->l_flags &= ~NTFSLOG_NO_LAST_LSN; ++ log->last_lsn = le64_to_cpu(cur_page->record_hdr.last_end_lsn); ++ log->ra->current_lsn = cur_page->record_hdr.last_end_lsn; ++ ++ if (log->record_header_len <= ++ log->page_size - ++ le16_to_cpu(cur_page->record_hdr.next_record_off)) { ++ log->l_flags |= NTFSLOG_REUSE_TAIL; ++ log->next_page = curpage_off; ++ } else { ++ log->l_flags &= ~NTFSLOG_REUSE_TAIL; ++ log->next_page = nextpage_off; ++ } ++ ++ if (wrapped_file) ++ log->l_flags |= NTFSLOG_WRAPPED; ++ ++ last_ok_lsn = le64_to_cpu(cur_page->record_hdr.last_end_lsn); ++ goto next_page_1; ++ } ++ ++ /* ++ * If we are at the expected first page of a transfer check to see ++ * if either tail copy is at this offset ++ * If this page is the last page of a transfer, check if we wrote ++ * a subsequent tail copy ++ */ ++ if (page_cnt == page_pos || page_cnt == page_pos + 1) { ++ /* ++ * Check if the offset matches either the first or second ++ * tail copy. It is possible it will match both ++ */ ++ if (curpage_off == final_off) ++ tail_page = first_tail; ++ ++ /* ++ * If we already matched on the first page then ++ * check the ending lsn's. ++ */ ++ if (curpage_off == second_off) { ++ if (!tail_page || ++ (second_tail && ++ le64_to_cpu(second_tail->record_hdr.last_end_lsn) > ++ le64_to_cpu(first_tail->record_hdr ++ .last_end_lsn))) { ++ tail_page = second_tail; ++ } ++ } ++ } ++ ++use_tail_page: ++ if (tail_page) { ++ /* we have a candidate for a tail copy */ ++ lsn_cur = le64_to_cpu(tail_page->record_hdr.last_end_lsn); ++ ++ if (last_ok_lsn < lsn_cur) { ++ /* ++ * If the sequence number is not expected, ++ * then don't use the tail copy ++ */ ++ if (expected_seq != (lsn_cur >> log->file_data_bits)) ++ tail_page = NULL; ++ } else if (last_ok_lsn > lsn_cur) { ++ /* ++ * If the last lsn is greater than the one on ++ * this page then forget this tail ++ */ ++ tail_page = NULL; ++ } ++ } ++ ++ /* If we have an error on the current page, we will break of this loop */ ++ if (err || usa_error) ++ goto check_tail; ++ ++ /* ++ * Done if the last lsn on this page doesn't match the previous known ++ * last lsn or the sequence number is not expected ++ */ ++ lsn_cur = le64_to_cpu(page->rhdr.lsn); ++ if (last_ok_lsn != lsn_cur && ++ expected_seq != (lsn_cur >> log->file_data_bits)) { ++ goto check_tail; ++ } ++ ++ /* ++ * Check that the page position and page count values are correct ++ * If this is the first page of a transfer the position must be 1 ++ * and the count will be unknown ++ */ ++ if (page_cnt == page_pos) { ++ if (page->page_pos != cpu_to_le16(1) && ++ (!reuse_page || page->page_pos != page->page_count)) { ++ /* ++ * If the current page is the first page we are ++ * looking at and we are reusing this page then ++ * it can be either the first or last page of a ++ * transfer. Otherwise it can only be the first. ++ */ ++ goto check_tail; ++ } ++ } else if (le16_to_cpu(page->page_count) != page_cnt || ++ le16_to_cpu(page->page_pos) != page_pos + 1) { ++ /* ++ * The page position better be 1 more than the last page ++ * position and the page count better match ++ */ ++ goto check_tail; ++ } ++ ++ /* ++ * We have a valid page the file and may have a valid page ++ * the tail copy area ++ * If the tail page was written after the page the file then ++ * break of the loop ++ */ ++ if (tail_page && ++ le64_to_cpu(tail_page->record_hdr.last_end_lsn) > lsn_cur) { ++ /* Remember if we will replace the page */ ++ replace_page = true; ++ goto check_tail; ++ } ++ ++ tail_page = NULL; ++ ++ if (is_log_record_end(page)) { ++ /* ++ * Since we have read this page we know the sequence number ++ * is the same as our expected value ++ */ ++ log->seq_num = expected_seq; ++ log->last_lsn = le64_to_cpu(page->record_hdr.last_end_lsn); ++ log->ra->current_lsn = page->record_hdr.last_end_lsn; ++ log->l_flags &= ~NTFSLOG_NO_LAST_LSN; ++ ++ /* ++ * If there is room on this page for another header then ++ * remember we want to reuse the page ++ */ ++ if (log->record_header_len <= ++ log->page_size - ++ le16_to_cpu(page->record_hdr.next_record_off)) { ++ log->l_flags |= NTFSLOG_REUSE_TAIL; ++ log->next_page = curpage_off; ++ } else { ++ log->l_flags &= ~NTFSLOG_REUSE_TAIL; ++ log->next_page = nextpage_off; ++ } ++ ++ /* Remember if we wrapped the log file */ ++ if (wrapped_file) ++ log->l_flags |= NTFSLOG_WRAPPED; ++ } ++ ++ /* ++ * Remember the last page count and position. ++ * Also remember the last known lsn ++ */ ++ page_cnt = le16_to_cpu(page->page_count); ++ page_pos = le16_to_cpu(page->page_pos); ++ last_ok_lsn = le64_to_cpu(page->rhdr.lsn); ++ ++next_page_1: ++ ++ if (wrapped) { ++ expected_seq += 1; ++ wrapped_file = 1; ++ } ++ ++ curpage_off = nextpage_off; ++ ntfs_free(page); ++ page = NULL; ++ reuse_page = 0; ++ goto next_page; ++ ++check_tail: ++ if (tail_page) { ++ log->seq_num = expected_seq; ++ log->last_lsn = le64_to_cpu(tail_page->record_hdr.last_end_lsn); ++ log->ra->current_lsn = tail_page->record_hdr.last_end_lsn; ++ log->l_flags &= ~NTFSLOG_NO_LAST_LSN; ++ ++ if (log->page_size - ++ le16_to_cpu( ++ tail_page->record_hdr.next_record_off) >= ++ log->record_header_len) { ++ log->l_flags |= NTFSLOG_REUSE_TAIL; ++ log->next_page = curpage_off; ++ } else { ++ log->l_flags &= ~NTFSLOG_REUSE_TAIL; ++ log->next_page = nextpage_off; ++ } ++ ++ if (wrapped) ++ log->l_flags |= NTFSLOG_WRAPPED; ++ } ++ ++ /* Remember that the partial IO will start at the next page */ ++ second_off = nextpage_off; ++ ++ /* ++ * If the next page is the first page of the file then update ++ * the sequence number for log records which begon the next page ++ */ ++ if (wrapped) ++ expected_seq += 1; ++ ++ /* ++ * If we have a tail copy or are performing single page I/O we can ++ * immediately look at the next page ++ */ ++ if (replace_page || (log->ra->flags & RESTART_SINGLE_PAGE_IO)) { ++ page_cnt = 2; ++ page_pos = 1; ++ goto check_valid; ++ } ++ ++ if (page_pos != page_cnt) ++ goto check_valid; ++ /* ++ * If the next page causes us to wrap to the beginning of the log ++ * file then we know which page to check next. ++ */ ++ if (wrapped) { ++ page_cnt = 2; ++ page_pos = 1; ++ goto check_valid; ++ } ++ ++ cur_pos = 2; ++ ++next_test_page: ++ ntfs_free(tst_page); ++ tst_page = NULL; ++ ++ /* Walk through the file, reading log pages */ ++ err = read_log_page(log, nextpage_off, &tst_page, &usa_error); ++ ++ /* ++ * If we get a USA error then assume that we correctly found ++ * the end of the original transfer ++ */ ++ if (usa_error) ++ goto file_is_valid; ++ ++ /* ++ * If we were able to read the page, we examine it to see if it ++ * is the same or different Io block ++ */ ++ if (err) ++ goto next_test_page_1; ++ ++ if (le16_to_cpu(tst_page->page_pos) == cur_pos && ++ check_subseq_log_page(log, tst_page, nextpage_off, expected_seq)) { ++ page_cnt = le16_to_cpu(tst_page->page_count) + 1; ++ page_pos = le16_to_cpu(tst_page->page_pos); ++ goto check_valid; ++ } else { ++ goto file_is_valid; ++ } ++ ++next_test_page_1: ++ ++ nextpage_off = next_page_off(log, curpage_off); ++ wrapped = nextpage_off == log->first_page; ++ ++ if (wrapped) { ++ expected_seq += 1; ++ page_cnt = 2; ++ page_pos = 1; ++ } ++ ++ cur_pos += 1; ++ part_io_count += 1; ++ if (!wrapped) ++ goto next_test_page; ++ ++check_valid: ++ /* Skip over the remaining pages this transfer */ ++ remain_pages = page_cnt - page_pos - 1; ++ part_io_count += remain_pages; ++ ++ while (remain_pages--) { ++ nextpage_off = next_page_off(log, curpage_off); ++ wrapped = nextpage_off == log->first_page; ++ ++ if (wrapped) ++ expected_seq += 1; ++ } ++ ++ /* Call our routine to check this log page */ ++ ntfs_free(tst_page); ++ tst_page = NULL; ++ ++ err = read_log_page(log, nextpage_off, &tst_page, &usa_error); ++ if (!err && !usa_error && ++ check_subseq_log_page(log, tst_page, nextpage_off, expected_seq)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++file_is_valid: ++ ++ /* We have a valid file */ ++ if (page_off1 || tail_page) { ++ struct RECORD_PAGE_HDR *tmp_page; ++ ++ if (sb_rdonly(log->ni->mi.sbi->sb)) { ++ err = -EROFS; ++ goto out; ++ } ++ ++ if (page_off1) { ++ tmp_page = Add2Ptr(page_bufs, page_off1 - page_off); ++ tails -= (page_off1 - page_off) / log->page_size; ++ if (!tail_page) ++ tails -= 1; ++ } else { ++ tmp_page = tail_page; ++ tails = 1; ++ } ++ ++ while (tails--) { ++ u64 off = hdr_file_off(log, tmp_page); ++ ++ if (!page) { ++ page = ntfs_malloc(log->page_size); ++ if (!page) ++ return -ENOMEM; ++ } ++ ++ /* ++ * Correct page and copy the data from this page ++ * into it and flush it to disk ++ */ ++ memcpy(page, tmp_page, log->page_size); ++ ++ /* Fill last flushed lsn value flush the page */ ++ if (log->major_ver < 2) ++ page->rhdr.lsn = page->record_hdr.last_end_lsn; ++ else ++ page->file_off = 0; ++ ++ page->page_pos = page->page_count = cpu_to_le16(1); ++ ++ ntfs_fix_pre_write(&page->rhdr, log->page_size); ++ ++ err = ntfs_sb_write_run(log->ni->mi.sbi, ++ &log->ni->file.run, off, page, ++ log->page_size); ++ ++ if (err) ++ goto out; ++ ++ if (part_io_count && second_off == off) { ++ second_off += log->page_size; ++ part_io_count -= 1; ++ } ++ ++ tmp_page = Add2Ptr(tmp_page, log->page_size); ++ } ++ } ++ ++ if (part_io_count) { ++ if (sb_rdonly(log->ni->mi.sbi->sb)) { ++ err = -EROFS; ++ goto out; ++ } ++ } ++ ++out: ++ ntfs_free(second_tail); ++ ntfs_free(first_tail); ++ ntfs_free(page); ++ ntfs_free(tst_page); ++ ntfs_free(page_bufs); ++ ++ return err; ++} ++ ++/* ++ * read_log_rec_buf ++ * ++ * copies a log record from the file to a buffer ++ * The log record may span several log pages and may even wrap the file ++ */ ++static int read_log_rec_buf(struct ntfs_log *log, ++ const struct LFS_RECORD_HDR *rh, void *buffer) ++{ ++ int err; ++ struct RECORD_PAGE_HDR *ph = NULL; ++ u64 lsn = le64_to_cpu(rh->this_lsn); ++ u32 vbo = lsn_to_vbo(log, lsn) & ~log->page_mask; ++ u32 off = lsn_to_page_off(log, lsn) + log->record_header_len; ++ u32 data_len = le32_to_cpu(rh->client_data_len); ++ ++ /* ++ * While there are more bytes to transfer, ++ * we continue to attempt to perform the read ++ */ ++ for (;;) { ++ bool usa_error; ++ u32 tail = log->page_size - off; ++ ++ if (tail >= data_len) ++ tail = data_len; ++ ++ data_len -= tail; ++ ++ err = read_log_page(log, vbo, &ph, &usa_error); ++ if (err) ++ goto out; ++ ++ /* ++ * The last lsn on this page better be greater or equal ++ * to the lsn we are copying ++ */ ++ if (lsn > le64_to_cpu(ph->rhdr.lsn)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ memcpy(buffer, Add2Ptr(ph, off), tail); ++ ++ /* If there are no more bytes to transfer, we exit the loop */ ++ if (!data_len) { ++ if (!is_log_record_end(ph) || ++ lsn > le64_to_cpu(ph->record_hdr.last_end_lsn)) { ++ err = -EINVAL; ++ goto out; ++ } ++ break; ++ } ++ ++ if (ph->rhdr.lsn == ph->record_hdr.last_end_lsn || ++ lsn > le64_to_cpu(ph->rhdr.lsn)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ vbo = next_page_off(log, vbo); ++ off = log->data_off; ++ ++ /* ++ * adjust our pointer the user's buffer to transfer ++ * the next block to ++ */ ++ buffer = Add2Ptr(buffer, tail); ++ } ++ ++out: ++ ntfs_free(ph); ++ return err; ++} ++ ++static int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_, ++ u64 *lsn) ++{ ++ int err; ++ struct LFS_RECORD_HDR *rh = NULL; ++ const struct CLIENT_REC *cr = ++ Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off)); ++ u64 lsnr, lsnc = le64_to_cpu(cr->restart_lsn); ++ u32 len; ++ struct NTFS_RESTART *rst; ++ ++ *lsn = 0; ++ *rst_ = NULL; ++ ++ /* If the client doesn't have a restart area, go ahead and exit now */ ++ if (!lsnc) ++ return 0; ++ ++ err = read_log_page(log, lsn_to_vbo(log, lsnc), ++ (struct RECORD_PAGE_HDR **)&rh, NULL); ++ if (err) ++ return err; ++ ++ rst = NULL; ++ lsnr = le64_to_cpu(rh->this_lsn); ++ ++ if (lsnc != lsnr) { ++ /* If the lsn values don't match, then the disk is corrupt */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ *lsn = lsnr; ++ len = le32_to_cpu(rh->client_data_len); ++ ++ if (!len) { ++ err = 0; ++ goto out; ++ } ++ ++ if (len < sizeof(struct NTFS_RESTART)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ rst = ntfs_malloc(len); ++ if (!rst) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Copy the data into the 'rst' buffer */ ++ err = read_log_rec_buf(log, rh, rst); ++ if (err) ++ goto out; ++ ++ *rst_ = rst; ++ rst = NULL; ++ ++out: ++ ntfs_free(rh); ++ ntfs_free(rst); ++ ++ return err; ++} ++ ++static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) ++{ ++ int err; ++ struct LFS_RECORD_HDR *rh = lcb->lrh; ++ u32 rec_len, len; ++ ++ /* Read the record header for this lsn */ ++ if (!rh) { ++ err = read_log_page(log, lsn_to_vbo(log, lsn), ++ (struct RECORD_PAGE_HDR **)&rh, NULL); ++ ++ lcb->lrh = rh; ++ if (err) ++ return err; ++ } ++ ++ /* ++ * If the lsn the log record doesn't match the desired ++ * lsn then the disk is corrupt ++ */ ++ if (lsn != le64_to_cpu(rh->this_lsn)) ++ return -EINVAL; ++ ++ len = le32_to_cpu(rh->client_data_len); ++ ++ /* ++ * check that the length field isn't greater than the total ++ * available space the log file ++ */ ++ rec_len = len + log->record_header_len; ++ if (rec_len >= log->total_avail) ++ return -EINVAL; ++ ++ /* ++ * If the entire log record is on this log page, ++ * put a pointer to the log record the context block ++ */ ++ if (rh->flags & LOG_RECORD_MULTI_PAGE) { ++ void *lr = ntfs_malloc(len); ++ ++ if (!lr) ++ return -ENOMEM; ++ ++ lcb->log_rec = lr; ++ lcb->alloc = true; ++ ++ /* Copy the data into the buffer returned */ ++ err = read_log_rec_buf(log, rh, lr); ++ if (err) ++ return err; ++ } else { ++ /* If beyond the end of the current page -> an error */ ++ u32 page_off = lsn_to_page_off(log, lsn); ++ ++ if (page_off + len + log->record_header_len > log->page_size) ++ return -EINVAL; ++ ++ lcb->log_rec = Add2Ptr(rh, sizeof(struct LFS_RECORD_HDR)); ++ lcb->alloc = false; ++ } ++ ++ return 0; ++} ++ ++/* ++ * read_log_rec_lcb ++ * ++ * initiates the query operation. ++ */ ++static int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode, ++ struct lcb **lcb_) ++{ ++ int err; ++ const struct CLIENT_REC *cr; ++ struct lcb *lcb; ++ ++ switch (ctx_mode) { ++ case lcb_ctx_undo_next: ++ case lcb_ctx_prev: ++ case lcb_ctx_next: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* check that the given lsn is the legal range for this client */ ++ cr = Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off)); ++ ++ if (!verify_client_lsn(log, cr, lsn)) ++ return -EINVAL; ++ ++ lcb = ntfs_zalloc(sizeof(struct lcb)); ++ if (!lcb) ++ return -ENOMEM; ++ lcb->client = log->client_id; ++ lcb->ctx_mode = ctx_mode; ++ ++ /* Find the log record indicated by the given lsn */ ++ err = find_log_rec(log, lsn, lcb); ++ if (err) ++ goto out; ++ ++ *lcb_ = lcb; ++ return 0; ++ ++out: ++ lcb_put(lcb); ++ *lcb_ = NULL; ++ return err; ++} ++ ++/* ++ * find_client_next_lsn ++ * ++ * attempt to find the next lsn to return to a client based on the context mode. ++ */ ++static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) ++{ ++ int err; ++ u64 next_lsn; ++ struct LFS_RECORD_HDR *hdr; ++ ++ hdr = lcb->lrh; ++ *lsn = 0; ++ ++ if (lcb_ctx_next != lcb->ctx_mode) ++ goto check_undo_next; ++ ++ /* Loop as long as another lsn can be found */ ++ for (;;) { ++ u64 current_lsn; ++ ++ err = next_log_lsn(log, hdr, ¤t_lsn); ++ if (err) ++ goto out; ++ ++ if (!current_lsn) ++ break; ++ ++ if (hdr != lcb->lrh) ++ ntfs_free(hdr); ++ ++ hdr = NULL; ++ err = read_log_page(log, lsn_to_vbo(log, current_lsn), ++ (struct RECORD_PAGE_HDR **)&hdr, NULL); ++ if (err) ++ goto out; ++ ++ if (memcmp(&hdr->client, &lcb->client, ++ sizeof(struct CLIENT_ID))) { ++ /*err = -EINVAL; */ ++ } else if (LfsClientRecord == hdr->record_type) { ++ ntfs_free(lcb->lrh); ++ lcb->lrh = hdr; ++ *lsn = current_lsn; ++ return 0; ++ } ++ } ++ ++out: ++ if (hdr != lcb->lrh) ++ ntfs_free(hdr); ++ return err; ++ ++check_undo_next: ++ if (lcb_ctx_undo_next == lcb->ctx_mode) ++ next_lsn = le64_to_cpu(hdr->client_undo_next_lsn); ++ else if (lcb_ctx_prev == lcb->ctx_mode) ++ next_lsn = le64_to_cpu(hdr->client_prev_lsn); ++ else ++ return 0; ++ ++ if (!next_lsn) ++ return 0; ++ ++ if (!verify_client_lsn( ++ log, Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off)), ++ next_lsn)) ++ return 0; ++ ++ hdr = NULL; ++ err = read_log_page(log, lsn_to_vbo(log, next_lsn), ++ (struct RECORD_PAGE_HDR **)&hdr, NULL); ++ if (err) ++ return err; ++ ntfs_free(lcb->lrh); ++ lcb->lrh = hdr; ++ ++ *lsn = next_lsn; ++ ++ return 0; ++} ++ ++static int read_next_log_rec(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) ++{ ++ int err; ++ ++ err = find_client_next_lsn(log, lcb, lsn); ++ if (err) ++ return err; ++ ++ if (!*lsn) ++ return 0; ++ ++ if (lcb->alloc) ++ ntfs_free(lcb->log_rec); ++ ++ lcb->log_rec = NULL; ++ lcb->alloc = false; ++ ntfs_free(lcb->lrh); ++ lcb->lrh = NULL; ++ ++ return find_log_rec(log, *lsn, lcb); ++} ++ ++static inline bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes) ++{ ++ __le16 mask; ++ u32 min_de, de_off, used, total; ++ const struct NTFS_DE *e; ++ ++ if (hdr_has_subnode(hdr)) { ++ min_de = sizeof(struct NTFS_DE) + sizeof(u64); ++ mask = NTFS_IE_HAS_SUBNODES; ++ } else { ++ min_de = sizeof(struct NTFS_DE); ++ mask = 0; ++ } ++ ++ de_off = le32_to_cpu(hdr->de_off); ++ used = le32_to_cpu(hdr->used); ++ total = le32_to_cpu(hdr->total); ++ ++ if (de_off > bytes - min_de || used > bytes || total > bytes || ++ de_off + min_de > used || used > total) { ++ return false; ++ } ++ ++ e = Add2Ptr(hdr, de_off); ++ for (;;) { ++ u16 esize = le16_to_cpu(e->size); ++ struct NTFS_DE *next = Add2Ptr(e, esize); ++ ++ if (esize < min_de || PtrOffset(hdr, next) > used || ++ (e->flags & NTFS_IE_HAS_SUBNODES) != mask) { ++ return false; ++ } ++ ++ if (de_is_last(e)) ++ break; ++ ++ e = next; ++ } ++ ++ return true; ++} ++ ++static inline bool check_index_buffer(const struct INDEX_BUFFER *ib, u32 bytes) ++{ ++ u16 fo; ++ const struct NTFS_RECORD_HEADER *r = &ib->rhdr; ++ ++ if (r->sign != NTFS_INDX_SIGNATURE) ++ return false; ++ ++ fo = (SECTOR_SIZE - ((bytes >> SECTOR_SHIFT) + 1) * sizeof(short)); ++ ++ if (le16_to_cpu(r->fix_off) > fo) ++ return false; ++ ++ if ((le16_to_cpu(r->fix_num) - 1) * SECTOR_SIZE != bytes) ++ return false; ++ ++ return check_index_header(&ib->ihdr, ++ bytes - offsetof(struct INDEX_BUFFER, ihdr)); ++} ++ ++static inline bool check_index_root(const struct ATTRIB *attr, ++ struct ntfs_sb_info *sbi) ++{ ++ bool ret; ++ const struct INDEX_ROOT *root = resident_data(attr); ++ u8 index_bits = le32_to_cpu(root->index_block_size) >= sbi->cluster_size ++ ? sbi->cluster_bits ++ : SECTOR_SHIFT; ++ u8 block_clst = root->index_block_clst; ++ ++ if (le32_to_cpu(attr->res.data_size) < sizeof(struct INDEX_ROOT) || ++ (root->type != ATTR_NAME && root->type != ATTR_ZERO) || ++ (root->type == ATTR_NAME && ++ root->rule != NTFS_COLLATION_TYPE_FILENAME) || ++ (le32_to_cpu(root->index_block_size) != ++ (block_clst << index_bits)) || ++ (block_clst != 1 && block_clst != 2 && block_clst != 4 && ++ block_clst != 8 && block_clst != 0x10 && block_clst != 0x20 && ++ block_clst != 0x40 && block_clst != 0x80)) { ++ return false; ++ } ++ ++ ret = check_index_header(&root->ihdr, ++ le32_to_cpu(attr->res.data_size) - ++ offsetof(struct INDEX_ROOT, ihdr)); ++ return ret; ++} ++ ++static inline bool check_attr(const struct MFT_REC *rec, ++ const struct ATTRIB *attr, ++ struct ntfs_sb_info *sbi) ++{ ++ u32 asize = le32_to_cpu(attr->size); ++ u32 rsize = 0; ++ u64 dsize, svcn, evcn; ++ u16 run_off; ++ ++ /* Check the fixed part of the attribute record header */ ++ if (asize >= sbi->record_size || ++ asize + PtrOffset(rec, attr) >= sbi->record_size || ++ (attr->name_len && ++ le16_to_cpu(attr->name_off) + attr->name_len * sizeof(short) > ++ asize)) { ++ return false; ++ } ++ ++ /* Check the attribute fields */ ++ switch (attr->non_res) { ++ case 0: ++ rsize = le32_to_cpu(attr->res.data_size); ++ if (rsize >= asize || ++ le16_to_cpu(attr->res.data_off) + rsize > asize) { ++ return false; ++ } ++ break; ++ ++ case 1: ++ dsize = le64_to_cpu(attr->nres.data_size); ++ svcn = le64_to_cpu(attr->nres.svcn); ++ evcn = le64_to_cpu(attr->nres.evcn); ++ run_off = le16_to_cpu(attr->nres.run_off); ++ ++ if (svcn > evcn + 1 || run_off >= asize || ++ le64_to_cpu(attr->nres.valid_size) > dsize || ++ dsize > le64_to_cpu(attr->nres.alloc_size)) { ++ return false; ++ } ++ ++ if (run_unpack(NULL, sbi, 0, svcn, evcn, svcn, ++ Add2Ptr(attr, run_off), asize - run_off) < 0) { ++ return false; ++ } ++ ++ return true; ++ ++ default: ++ return false; ++ } ++ ++ switch (attr->type) { ++ case ATTR_NAME: ++ if (fname_full_size(Add2Ptr( ++ attr, le16_to_cpu(attr->res.data_off))) > asize) { ++ return false; ++ } ++ break; ++ ++ case ATTR_ROOT: ++ return check_index_root(attr, sbi); ++ ++ case ATTR_STD: ++ if (rsize < sizeof(struct ATTR_STD_INFO5) && ++ rsize != sizeof(struct ATTR_STD_INFO)) { ++ return false; ++ } ++ break; ++ ++ case ATTR_LIST: ++ case ATTR_ID: ++ case ATTR_SECURE: ++ case ATTR_LABEL: ++ case ATTR_VOL_INFO: ++ case ATTR_DATA: ++ case ATTR_ALLOC: ++ case ATTR_BITMAP: ++ case ATTR_REPARSE: ++ case ATTR_EA_INFO: ++ case ATTR_EA: ++ case ATTR_PROPERTYSET: ++ case ATTR_LOGGED_UTILITY_STREAM: ++ break; ++ ++ default: ++ return false; ++ } ++ ++ return true; ++} ++ ++static inline bool check_file_record(const struct MFT_REC *rec, ++ const struct MFT_REC *rec2, ++ struct ntfs_sb_info *sbi) ++{ ++ const struct ATTRIB *attr; ++ u16 fo = le16_to_cpu(rec->rhdr.fix_off); ++ u16 fn = le16_to_cpu(rec->rhdr.fix_num); ++ u16 ao = le16_to_cpu(rec->attr_off); ++ u32 rs = sbi->record_size; ++ ++ /* check the file record header for consistency */ ++ if (rec->rhdr.sign != NTFS_FILE_SIGNATURE || ++ fo > (SECTOR_SIZE - ((rs >> SECTOR_SHIFT) + 1) * sizeof(short)) || ++ (fn - 1) * SECTOR_SIZE != rs || ao < MFTRECORD_FIXUP_OFFSET_1 || ++ ao > sbi->record_size - SIZEOF_RESIDENT || !is_rec_inuse(rec) || ++ le32_to_cpu(rec->total) != rs) { ++ return false; ++ } ++ ++ /* Loop to check all of the attributes */ ++ for (attr = Add2Ptr(rec, ao); attr->type != ATTR_END; ++ attr = Add2Ptr(attr, le32_to_cpu(attr->size))) { ++ if (check_attr(rec, attr, sbi)) ++ continue; ++ return false; ++ } ++ ++ return true; ++} ++ ++static inline int check_lsn(const struct NTFS_RECORD_HEADER *hdr, ++ const u64 *rlsn) ++{ ++ u64 lsn; ++ ++ if (!rlsn) ++ return true; ++ ++ lsn = le64_to_cpu(hdr->lsn); ++ ++ if (hdr->sign == NTFS_HOLE_SIGNATURE) ++ return false; ++ ++ if (*rlsn > lsn) ++ return true; ++ ++ return false; ++} ++ ++static inline bool check_if_attr(const struct MFT_REC *rec, ++ const struct LOG_REC_HDR *lrh) ++{ ++ u16 ro = le16_to_cpu(lrh->record_off); ++ u16 o = le16_to_cpu(rec->attr_off); ++ const struct ATTRIB *attr = Add2Ptr(rec, o); ++ ++ while (o < ro) { ++ u32 asize; ++ ++ if (attr->type == ATTR_END) ++ break; ++ ++ asize = le32_to_cpu(attr->size); ++ if (!asize) ++ break; ++ ++ o += asize; ++ attr = Add2Ptr(attr, asize); ++ } ++ ++ return o == ro; ++} ++ ++static inline bool check_if_index_root(const struct MFT_REC *rec, ++ const struct LOG_REC_HDR *lrh) ++{ ++ u16 ro = le16_to_cpu(lrh->record_off); ++ u16 o = le16_to_cpu(rec->attr_off); ++ const struct ATTRIB *attr = Add2Ptr(rec, o); ++ ++ while (o < ro) { ++ u32 asize; ++ ++ if (attr->type == ATTR_END) ++ break; ++ ++ asize = le32_to_cpu(attr->size); ++ if (!asize) ++ break; ++ ++ o += asize; ++ attr = Add2Ptr(attr, asize); ++ } ++ ++ return o == ro && attr->type == ATTR_ROOT; ++} ++ ++static inline bool check_if_root_index(const struct ATTRIB *attr, ++ const struct INDEX_HDR *hdr, ++ const struct LOG_REC_HDR *lrh) ++{ ++ u16 ao = le16_to_cpu(lrh->attr_off); ++ u32 de_off = le32_to_cpu(hdr->de_off); ++ u32 o = PtrOffset(attr, hdr) + de_off; ++ const struct NTFS_DE *e = Add2Ptr(hdr, de_off); ++ u32 asize = le32_to_cpu(attr->size); ++ ++ while (o < ao) { ++ u16 esize; ++ ++ if (o >= asize) ++ break; ++ ++ esize = le16_to_cpu(e->size); ++ if (!esize) ++ break; ++ ++ o += esize; ++ e = Add2Ptr(e, esize); ++ } ++ ++ return o == ao; ++} ++ ++static inline bool check_if_alloc_index(const struct INDEX_HDR *hdr, ++ u32 attr_off) ++{ ++ u32 de_off = le32_to_cpu(hdr->de_off); ++ u32 o = offsetof(struct INDEX_BUFFER, ihdr) + de_off; ++ const struct NTFS_DE *e = Add2Ptr(hdr, de_off); ++ u32 used = le32_to_cpu(hdr->used); ++ ++ while (o < attr_off) { ++ u16 esize; ++ ++ if (de_off >= used) ++ break; ++ ++ esize = le16_to_cpu(e->size); ++ if (!esize) ++ break; ++ ++ o += esize; ++ de_off += esize; ++ e = Add2Ptr(e, esize); ++ } ++ ++ return o == attr_off; ++} ++ ++static inline void change_attr_size(struct MFT_REC *rec, struct ATTRIB *attr, ++ u32 nsize) ++{ ++ u32 asize = le32_to_cpu(attr->size); ++ int dsize = nsize - asize; ++ u8 *next = Add2Ptr(attr, asize); ++ u32 used = le32_to_cpu(rec->used); ++ ++ memmove(Add2Ptr(attr, nsize), next, used - PtrOffset(rec, next)); ++ ++ rec->used = cpu_to_le32(used + dsize); ++ attr->size = cpu_to_le32(nsize); ++} ++ ++struct OpenAttr { ++ struct ATTRIB *attr; ++ struct runs_tree *run1; ++ struct runs_tree run0; ++ struct ntfs_inode *ni; ++ // CLST rno; ++}; ++ ++/* Returns 0 if 'attr' has the same type and name */ ++static inline int cmp_type_and_name(const struct ATTRIB *a1, ++ const struct ATTRIB *a2) ++{ ++ return a1->type != a2->type || a1->name_len != a2->name_len || ++ (a1->name_len && memcmp(attr_name(a1), attr_name(a2), ++ a1->name_len * sizeof(short))); ++} ++ ++static struct OpenAttr *find_loaded_attr(struct ntfs_log *log, ++ const struct ATTRIB *attr, CLST rno) ++{ ++ struct OPEN_ATTR_ENRTY *oe = NULL; ++ ++ while ((oe = enum_rstbl(log->open_attr_tbl, oe))) { ++ struct OpenAttr *op_attr; ++ ++ if (ino_get(&oe->ref) != rno) ++ continue; ++ ++ op_attr = (struct OpenAttr *)oe->ptr; ++ if (!cmp_type_and_name(op_attr->attr, attr)) ++ return op_attr; ++ } ++ return NULL; ++} ++ ++static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi, ++ enum ATTR_TYPE type, u64 size, ++ const u16 *name, size_t name_len, ++ __le16 flags) ++{ ++ struct ATTRIB *attr; ++ u32 name_size = QuadAlign(name_len * sizeof(short)); ++ bool is_ext = flags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED); ++ u32 asize = name_size + ++ (is_ext ? SIZEOF_NONRESIDENT_EX : SIZEOF_NONRESIDENT); ++ ++ attr = ntfs_zalloc(asize); ++ if (!attr) ++ return NULL; ++ ++ attr->type = type; ++ attr->size = cpu_to_le32(asize); ++ attr->flags = flags; ++ attr->non_res = 1; ++ attr->name_len = name_len; ++ ++ attr->nres.evcn = cpu_to_le64((u64)bytes_to_cluster(sbi, size) - 1); ++ attr->nres.alloc_size = cpu_to_le64(ntfs_up_cluster(sbi, size)); ++ attr->nres.data_size = cpu_to_le64(size); ++ attr->nres.valid_size = attr->nres.data_size; ++ if (is_ext) { ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ if (is_attr_compressed(attr)) ++ attr->nres.c_unit = COMPRESSION_UNIT; ++ ++ attr->nres.run_off = ++ cpu_to_le16(SIZEOF_NONRESIDENT_EX + name_size); ++ memcpy(Add2Ptr(attr, SIZEOF_NONRESIDENT_EX), name, ++ name_len * sizeof(short)); ++ } else { ++ attr->name_off = SIZEOF_NONRESIDENT_LE; ++ attr->nres.run_off = ++ cpu_to_le16(SIZEOF_NONRESIDENT + name_size); ++ memcpy(Add2Ptr(attr, SIZEOF_NONRESIDENT), name, ++ name_len * sizeof(short)); ++ } ++ ++ return attr; ++} ++ ++/* ++ * do_action ++ * ++ * common routine for the Redo and Undo Passes ++ * If rlsn is NULL then undo ++ */ ++static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, ++ const struct LOG_REC_HDR *lrh, u32 op, void *data, ++ u32 dlen, u32 rec_len, const u64 *rlsn) ++{ ++ int err = 0; ++ struct ntfs_sb_info *sbi = log->ni->mi.sbi; ++ struct inode *inode = NULL, *inode_parent; ++ struct mft_inode *mi = NULL, *mi2_child = NULL; ++ CLST rno = 0, rno_base = 0; ++ struct INDEX_BUFFER *ib = NULL; ++ struct MFT_REC *rec = NULL; ++ struct ATTRIB *attr = NULL, *attr2; ++ struct INDEX_HDR *hdr; ++ struct INDEX_ROOT *root; ++ struct NTFS_DE *e, *e1, *e2; ++ struct NEW_ATTRIBUTE_SIZES *new_sz; ++ struct ATTR_FILE_NAME *fname; ++ struct OpenAttr *oa, *oa2; ++ u32 nsize, t32, asize, used, esize, bmp_off, bmp_bits; ++ u16 id, id2; ++ u32 record_size = sbi->record_size; ++ u64 t64; ++ u16 roff = le16_to_cpu(lrh->record_off); ++ u16 aoff = le16_to_cpu(lrh->attr_off); ++ u64 lco = 0; ++ u64 cbo = (u64)le16_to_cpu(lrh->cluster_off) << SECTOR_SHIFT; ++ u64 tvo = le64_to_cpu(lrh->target_vcn) << sbi->cluster_bits; ++ u64 vbo = cbo + tvo; ++ void *buffer_le = NULL; ++ u32 bytes = 0; ++ bool a_dirty = false; ++ u16 data_off; ++ ++ oa = oe->ptr; ++ ++ /* Big switch to prepare */ ++ switch (op) { ++ /* ============================================================ ++ * Process MFT records, as described by the current log record ++ * ============================================================ ++ */ ++ case InitializeFileRecordSegment: ++ case DeallocateFileRecordSegment: ++ case WriteEndOfFileRecordSegment: ++ case CreateAttribute: ++ case DeleteAttribute: ++ case UpdateResidentValue: ++ case UpdateMappingPairs: ++ case SetNewAttributeSizes: ++ case AddIndexEntryRoot: ++ case DeleteIndexEntryRoot: ++ case SetIndexEntryVcnRoot: ++ case UpdateFileNameRoot: ++ case UpdateRecordDataRoot: ++ case ZeroEndOfFileRecord: ++ rno = vbo >> sbi->record_bits; ++ inode = ilookup(sbi->sb, rno); ++ if (inode) { ++ mi = &ntfs_i(inode)->mi; ++ } else if (op == InitializeFileRecordSegment) { ++ mi = ntfs_zalloc(sizeof(struct mft_inode)); ++ if (!mi) ++ return -ENOMEM; ++ err = mi_format_new(mi, sbi, rno, 0, false); ++ if (err) ++ goto out; ++ } else { ++ /* read from disk */ ++ err = mi_get(sbi, rno, &mi); ++ if (err) ++ return err; ++ } ++ rec = mi->mrec; ++ ++ if (op == DeallocateFileRecordSegment) ++ goto skip_load_parent; ++ ++ if (InitializeFileRecordSegment != op) { ++ if (rec->rhdr.sign == NTFS_BAAD_SIGNATURE) ++ goto dirty_vol; ++ if (!check_lsn(&rec->rhdr, rlsn)) ++ goto out; ++ if (!check_file_record(rec, NULL, sbi)) ++ goto dirty_vol; ++ attr = Add2Ptr(rec, roff); ++ } ++ ++ if (is_rec_base(rec) || InitializeFileRecordSegment == op) { ++ rno_base = rno; ++ goto skip_load_parent; ++ } ++ ++ rno_base = ino_get(&rec->parent_ref); ++ inode_parent = ntfs_iget5(sbi->sb, &rec->parent_ref, NULL); ++ if (IS_ERR(inode_parent)) ++ goto skip_load_parent; ++ ++ if (is_bad_inode(inode_parent)) { ++ iput(inode_parent); ++ goto skip_load_parent; ++ } ++ ++ if (ni_load_mi_ex(ntfs_i(inode_parent), rno, &mi2_child)) { ++ iput(inode_parent); ++ } else { ++ if (mi2_child->mrec != mi->mrec) ++ memcpy(mi2_child->mrec, mi->mrec, ++ sbi->record_size); ++ ++ if (inode) ++ iput(inode); ++ else if (mi) ++ mi_put(mi); ++ ++ inode = inode_parent; ++ mi = mi2_child; ++ rec = mi2_child->mrec; ++ attr = Add2Ptr(rec, roff); ++ } ++ ++skip_load_parent: ++ inode_parent = NULL; ++ break; ++ ++ /* ============================================================ ++ * Process attributes, as described by the current log record ++ * ============================================================ ++ */ ++ case UpdateNonresidentValue: ++ case AddIndexEntryAllocation: ++ case DeleteIndexEntryAllocation: ++ case WriteEndOfIndexBuffer: ++ case SetIndexEntryVcnAllocation: ++ case UpdateFileNameAllocation: ++ case SetBitsInNonresidentBitMap: ++ case ClearBitsInNonresidentBitMap: ++ case UpdateRecordDataAllocation: ++ attr = oa->attr; ++ bytes = UpdateNonresidentValue == op ? dlen : 0; ++ lco = (u64)le16_to_cpu(lrh->lcns_follow) << sbi->cluster_bits; ++ ++ if (attr->type == ATTR_ALLOC) { ++ t32 = le32_to_cpu(oe->bytes_per_index); ++ if (bytes < t32) ++ bytes = t32; ++ } ++ ++ if (!bytes) ++ bytes = lco - cbo; ++ ++ bytes += roff; ++ if (attr->type == ATTR_ALLOC) ++ bytes = (bytes + 511) & ~511; // align ++ ++ buffer_le = ntfs_malloc(bytes); ++ if (!buffer_le) ++ return -ENOMEM; ++ ++ err = ntfs_read_run_nb(sbi, oa->run1, vbo, buffer_le, bytes, ++ NULL); ++ if (err) ++ goto out; ++ ++ if (attr->type == ATTR_ALLOC && *(int *)buffer_le) ++ ntfs_fix_post_read(buffer_le, bytes, false); ++ break; ++ ++ default: ++ WARN_ON(1); ++ } ++ ++ /* Big switch to do operation */ ++ switch (op) { ++ case InitializeFileRecordSegment: ++ if (roff + dlen > record_size) ++ goto dirty_vol; ++ ++ memcpy(Add2Ptr(rec, roff), data, dlen); ++ mi->dirty = true; ++ break; ++ ++ case DeallocateFileRecordSegment: ++ clear_rec_inuse(rec); ++ le16_add_cpu(&rec->seq, 1); ++ mi->dirty = true; ++ break; ++ ++ case WriteEndOfFileRecordSegment: ++ attr2 = (struct ATTRIB *)data; ++ if (!check_if_attr(rec, lrh) || roff + dlen > record_size) ++ goto dirty_vol; ++ ++ memmove(attr, attr2, dlen); ++ rec->used = cpu_to_le32(QuadAlign(roff + dlen)); ++ ++ mi->dirty = true; ++ break; ++ ++ case CreateAttribute: ++ attr2 = (struct ATTRIB *)data; ++ asize = le32_to_cpu(attr2->size); ++ used = le32_to_cpu(rec->used); ++ ++ if (!check_if_attr(rec, lrh) || dlen < SIZEOF_RESIDENT || ++ !IsQuadAligned(asize) || ++ Add2Ptr(attr2, asize) > Add2Ptr(lrh, rec_len) || ++ dlen > record_size - used) { ++ goto dirty_vol; ++ } ++ ++ memmove(Add2Ptr(attr, asize), attr, used - roff); ++ memcpy(attr, attr2, asize); ++ ++ rec->used = cpu_to_le32(used + asize); ++ id = le16_to_cpu(rec->next_attr_id); ++ id2 = le16_to_cpu(attr2->id); ++ if (id <= id2) ++ rec->next_attr_id = cpu_to_le16(id2 + 1); ++ if (is_attr_indexed(attr)) ++ le16_add_cpu(&rec->hard_links, 1); ++ ++ oa2 = find_loaded_attr(log, attr, rno_base); ++ if (oa2) { ++ void *p2 = ntfs_memdup(attr, le32_to_cpu(attr->size)); ++ ++ if (p2) { ++ // run_close(oa2->run1); ++ ntfs_free(oa2->attr); ++ oa2->attr = p2; ++ } ++ } ++ ++ mi->dirty = true; ++ break; ++ ++ case DeleteAttribute: ++ asize = le32_to_cpu(attr->size); ++ used = le32_to_cpu(rec->used); ++ ++ if (!check_if_attr(rec, lrh)) ++ goto dirty_vol; ++ ++ rec->used = cpu_to_le32(used - asize); ++ if (is_attr_indexed(attr)) ++ le16_add_cpu(&rec->hard_links, -1); ++ ++ memmove(attr, Add2Ptr(attr, asize), used - asize - roff); ++ ++ mi->dirty = true; ++ break; ++ ++ case UpdateResidentValue: ++ nsize = aoff + dlen; ++ ++ if (!check_if_attr(rec, lrh)) ++ goto dirty_vol; ++ ++ asize = le32_to_cpu(attr->size); ++ used = le32_to_cpu(rec->used); ++ ++ if (lrh->redo_len == lrh->undo_len) { ++ if (nsize > asize) ++ goto dirty_vol; ++ goto move_data; ++ } ++ ++ if (nsize > asize && nsize - asize > record_size - used) ++ goto dirty_vol; ++ ++ nsize = QuadAlign(nsize); ++ data_off = le16_to_cpu(attr->res.data_off); ++ ++ if (nsize < asize) { ++ memmove(Add2Ptr(attr, aoff), data, dlen); ++ data = NULL; // To skip below memmove ++ } ++ ++ memmove(Add2Ptr(attr, nsize), Add2Ptr(attr, asize), ++ used - le16_to_cpu(lrh->record_off) - asize); ++ ++ rec->used = cpu_to_le32(used + nsize - asize); ++ attr->size = cpu_to_le32(nsize); ++ attr->res.data_size = cpu_to_le32(aoff + dlen - data_off); ++ ++move_data: ++ if (data) ++ memmove(Add2Ptr(attr, aoff), data, dlen); ++ ++ oa2 = find_loaded_attr(log, attr, rno_base); ++ if (oa2) { ++ void *p2 = ntfs_memdup(attr, le32_to_cpu(attr->size)); ++ ++ if (p2) { ++ // run_close(&oa2->run0); ++ oa2->run1 = &oa2->run0; ++ ntfs_free(oa2->attr); ++ oa2->attr = p2; ++ } ++ } ++ ++ mi->dirty = true; ++ break; ++ ++ case UpdateMappingPairs: ++ nsize = aoff + dlen; ++ asize = le32_to_cpu(attr->size); ++ used = le32_to_cpu(rec->used); ++ ++ if (!check_if_attr(rec, lrh) || !attr->non_res || ++ aoff < le16_to_cpu(attr->nres.run_off) || aoff > asize || ++ (nsize > asize && nsize - asize > record_size - used)) { ++ goto dirty_vol; ++ } ++ ++ nsize = QuadAlign(nsize); ++ ++ memmove(Add2Ptr(attr, nsize), Add2Ptr(attr, asize), ++ used - le16_to_cpu(lrh->record_off) - asize); ++ rec->used = cpu_to_le32(used + nsize - asize); ++ attr->size = cpu_to_le32(nsize); ++ memmove(Add2Ptr(attr, aoff), data, dlen); ++ ++ if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), ++ attr_run(attr), &t64)) { ++ goto dirty_vol; ++ } ++ ++ attr->nres.evcn = cpu_to_le64(t64); ++ oa2 = find_loaded_attr(log, attr, rno_base); ++ if (oa2 && oa2->attr->non_res) ++ oa2->attr->nres.evcn = attr->nres.evcn; ++ ++ mi->dirty = true; ++ break; ++ ++ case SetNewAttributeSizes: ++ new_sz = data; ++ if (!check_if_attr(rec, lrh) || !attr->non_res) ++ goto dirty_vol; ++ ++ attr->nres.alloc_size = new_sz->alloc_size; ++ attr->nres.data_size = new_sz->data_size; ++ attr->nres.valid_size = new_sz->valid_size; ++ ++ if (dlen >= sizeof(struct NEW_ATTRIBUTE_SIZES)) ++ attr->nres.total_size = new_sz->total_size; ++ ++ oa2 = find_loaded_attr(log, attr, rno_base); ++ if (oa2) { ++ void *p2 = ntfs_memdup(attr, le32_to_cpu(attr->size)); ++ ++ if (p2) { ++ ntfs_free(oa2->attr); ++ oa2->attr = p2; ++ } ++ } ++ mi->dirty = true; ++ break; ++ ++ case AddIndexEntryRoot: ++ e = (struct NTFS_DE *)data; ++ esize = le16_to_cpu(e->size); ++ root = resident_data(attr); ++ hdr = &root->ihdr; ++ used = le32_to_cpu(hdr->used); ++ ++ if (!check_if_index_root(rec, lrh) || ++ !check_if_root_index(attr, hdr, lrh) || ++ Add2Ptr(data, esize) > Add2Ptr(lrh, rec_len) || ++ esize > le32_to_cpu(rec->total) - le32_to_cpu(rec->used)) { ++ goto dirty_vol; ++ } ++ ++ e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); ++ ++ change_attr_size(rec, attr, le32_to_cpu(attr->size) + esize); ++ ++ memmove(Add2Ptr(e1, esize), e1, ++ PtrOffset(e1, Add2Ptr(hdr, used))); ++ memmove(e1, e, esize); ++ ++ le32_add_cpu(&attr->res.data_size, esize); ++ hdr->used = cpu_to_le32(used + esize); ++ le32_add_cpu(&hdr->total, esize); ++ ++ mi->dirty = true; ++ break; ++ ++ case DeleteIndexEntryRoot: ++ root = resident_data(attr); ++ hdr = &root->ihdr; ++ used = le32_to_cpu(hdr->used); ++ ++ if (!check_if_index_root(rec, lrh) || ++ !check_if_root_index(attr, hdr, lrh)) { ++ goto dirty_vol; ++ } ++ ++ e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); ++ esize = le16_to_cpu(e1->size); ++ e2 = Add2Ptr(e1, esize); ++ ++ memmove(e1, e2, PtrOffset(e2, Add2Ptr(hdr, used))); ++ ++ le32_sub_cpu(&attr->res.data_size, esize); ++ hdr->used = cpu_to_le32(used - esize); ++ le32_sub_cpu(&hdr->total, esize); ++ ++ change_attr_size(rec, attr, le32_to_cpu(attr->size) - esize); ++ ++ mi->dirty = true; ++ break; ++ ++ case SetIndexEntryVcnRoot: ++ root = resident_data(attr); ++ hdr = &root->ihdr; ++ ++ if (!check_if_index_root(rec, lrh) || ++ !check_if_root_index(attr, hdr, lrh)) { ++ goto dirty_vol; ++ } ++ ++ e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); ++ ++ de_set_vbn_le(e, *(__le64 *)data); ++ mi->dirty = true; ++ break; ++ ++ case UpdateFileNameRoot: ++ root = resident_data(attr); ++ hdr = &root->ihdr; ++ ++ if (!check_if_index_root(rec, lrh) || ++ !check_if_root_index(attr, hdr, lrh)) { ++ goto dirty_vol; ++ } ++ ++ e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); ++ fname = (struct ATTR_FILE_NAME *)(e + 1); ++ memmove(&fname->dup, data, sizeof(fname->dup)); // ++ mi->dirty = true; ++ break; ++ ++ case UpdateRecordDataRoot: ++ root = resident_data(attr); ++ hdr = &root->ihdr; ++ ++ if (!check_if_index_root(rec, lrh) || ++ !check_if_root_index(attr, hdr, lrh)) { ++ goto dirty_vol; ++ } ++ ++ e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off)); ++ ++ memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); ++ ++ mi->dirty = true; ++ break; ++ ++ case ZeroEndOfFileRecord: ++ if (roff + dlen > record_size) ++ goto dirty_vol; ++ ++ memset(attr, 0, dlen); ++ mi->dirty = true; ++ break; ++ ++ case UpdateNonresidentValue: ++ if (lco < cbo + roff + dlen) ++ goto dirty_vol; ++ ++ memcpy(Add2Ptr(buffer_le, roff), data, dlen); ++ ++ a_dirty = true; ++ if (attr->type == ATTR_ALLOC) ++ ntfs_fix_pre_write(buffer_le, bytes); ++ break; ++ ++ case AddIndexEntryAllocation: ++ ib = Add2Ptr(buffer_le, roff); ++ hdr = &ib->ihdr; ++ e = data; ++ esize = le16_to_cpu(e->size); ++ e1 = Add2Ptr(ib, aoff); ++ ++ if (is_baad(&ib->rhdr)) ++ goto dirty_vol; ++ if (!check_lsn(&ib->rhdr, rlsn)) ++ goto out; ++ ++ used = le32_to_cpu(hdr->used); ++ ++ if (!check_index_buffer(ib, bytes) || ++ !check_if_alloc_index(hdr, aoff) || ++ Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) || ++ used + esize > le32_to_cpu(hdr->total)) { ++ goto dirty_vol; ++ } ++ ++ memmove(Add2Ptr(e1, esize), e1, ++ PtrOffset(e1, Add2Ptr(hdr, used))); ++ memcpy(e1, e, esize); ++ ++ hdr->used = cpu_to_le32(used + esize); ++ ++ a_dirty = true; ++ ++ ntfs_fix_pre_write(&ib->rhdr, bytes); ++ break; ++ ++ case DeleteIndexEntryAllocation: ++ ib = Add2Ptr(buffer_le, roff); ++ hdr = &ib->ihdr; ++ e = Add2Ptr(ib, aoff); ++ esize = le16_to_cpu(e->size); ++ ++ if (is_baad(&ib->rhdr)) ++ goto dirty_vol; ++ if (!check_lsn(&ib->rhdr, rlsn)) ++ goto out; ++ ++ if (!check_index_buffer(ib, bytes) || ++ !check_if_alloc_index(hdr, aoff)) { ++ goto dirty_vol; ++ } ++ ++ e1 = Add2Ptr(e, esize); ++ nsize = esize; ++ used = le32_to_cpu(hdr->used); ++ ++ memmove(e, e1, PtrOffset(e1, Add2Ptr(hdr, used))); ++ ++ hdr->used = cpu_to_le32(used - nsize); ++ ++ a_dirty = true; ++ ++ ntfs_fix_pre_write(&ib->rhdr, bytes); ++ break; ++ ++ case WriteEndOfIndexBuffer: ++ ib = Add2Ptr(buffer_le, roff); ++ hdr = &ib->ihdr; ++ e = Add2Ptr(ib, aoff); ++ ++ if (is_baad(&ib->rhdr)) ++ goto dirty_vol; ++ if (!check_lsn(&ib->rhdr, rlsn)) ++ goto out; ++ if (!check_index_buffer(ib, bytes) || ++ !check_if_alloc_index(hdr, aoff) || ++ aoff + dlen > offsetof(struct INDEX_BUFFER, ihdr) + ++ le32_to_cpu(hdr->total)) { ++ goto dirty_vol; ++ } ++ ++ hdr->used = cpu_to_le32(dlen + PtrOffset(hdr, e)); ++ memmove(e, data, dlen); ++ ++ a_dirty = true; ++ ntfs_fix_pre_write(&ib->rhdr, bytes); ++ break; ++ ++ case SetIndexEntryVcnAllocation: ++ ib = Add2Ptr(buffer_le, roff); ++ hdr = &ib->ihdr; ++ e = Add2Ptr(ib, aoff); ++ ++ if (is_baad(&ib->rhdr)) ++ goto dirty_vol; ++ ++ if (!check_lsn(&ib->rhdr, rlsn)) ++ goto out; ++ if (!check_index_buffer(ib, bytes) || ++ !check_if_alloc_index(hdr, aoff)) { ++ goto dirty_vol; ++ } ++ ++ de_set_vbn_le(e, *(__le64 *)data); ++ ++ a_dirty = true; ++ ntfs_fix_pre_write(&ib->rhdr, bytes); ++ break; ++ ++ case UpdateFileNameAllocation: ++ ib = Add2Ptr(buffer_le, roff); ++ hdr = &ib->ihdr; ++ e = Add2Ptr(ib, aoff); ++ ++ if (is_baad(&ib->rhdr)) ++ goto dirty_vol; ++ ++ if (!check_lsn(&ib->rhdr, rlsn)) ++ goto out; ++ if (!check_index_buffer(ib, bytes) || ++ !check_if_alloc_index(hdr, aoff)) { ++ goto dirty_vol; ++ } ++ ++ fname = (struct ATTR_FILE_NAME *)(e + 1); ++ memmove(&fname->dup, data, sizeof(fname->dup)); ++ ++ a_dirty = true; ++ ntfs_fix_pre_write(&ib->rhdr, bytes); ++ break; ++ ++ case SetBitsInNonresidentBitMap: ++ bmp_off = ++ le32_to_cpu(((struct BITMAP_RANGE *)data)->bitmap_off); ++ bmp_bits = le32_to_cpu(((struct BITMAP_RANGE *)data)->bits); ++ ++ if (cbo + (bmp_off + 7) / 8 > lco || ++ cbo + ((bmp_off + bmp_bits + 7) / 8) > lco) { ++ goto dirty_vol; ++ } ++ ++ __bitmap_set(Add2Ptr(buffer_le, roff), bmp_off, bmp_bits); ++ a_dirty = true; ++ break; ++ ++ case ClearBitsInNonresidentBitMap: ++ bmp_off = ++ le32_to_cpu(((struct BITMAP_RANGE *)data)->bitmap_off); ++ bmp_bits = le32_to_cpu(((struct BITMAP_RANGE *)data)->bits); ++ ++ if (cbo + (bmp_off + 7) / 8 > lco || ++ cbo + ((bmp_off + bmp_bits + 7) / 8) > lco) { ++ goto dirty_vol; ++ } ++ ++ __bitmap_clear(Add2Ptr(buffer_le, roff), bmp_off, bmp_bits); ++ a_dirty = true; ++ break; ++ ++ case UpdateRecordDataAllocation: ++ ib = Add2Ptr(buffer_le, roff); ++ hdr = &ib->ihdr; ++ e = Add2Ptr(ib, aoff); ++ ++ if (is_baad(&ib->rhdr)) ++ goto dirty_vol; ++ ++ if (!check_lsn(&ib->rhdr, rlsn)) ++ goto out; ++ if (!check_index_buffer(ib, bytes) || ++ !check_if_alloc_index(hdr, aoff)) { ++ goto dirty_vol; ++ } ++ ++ memmove(Add2Ptr(e, le16_to_cpu(e->view.data_off)), data, dlen); ++ ++ a_dirty = true; ++ ntfs_fix_pre_write(&ib->rhdr, bytes); ++ break; ++ ++ default: ++ WARN_ON(1); ++ } ++ ++ if (rlsn) { ++ __le64 t64 = cpu_to_le64(*rlsn); ++ ++ if (rec) ++ rec->rhdr.lsn = t64; ++ if (ib) ++ ib->rhdr.lsn = t64; ++ } ++ ++ if (mi && mi->dirty) { ++ err = mi_write(mi, 0); ++ if (err) ++ goto out; ++ } ++ ++ if (a_dirty) { ++ attr = oa->attr; ++ err = ntfs_sb_write_run(sbi, oa->run1, vbo, buffer_le, bytes); ++ if (err) ++ goto out; ++ } ++ ++out: ++ ++ if (inode) ++ iput(inode); ++ else if (mi != mi2_child) ++ mi_put(mi); ++ ++ ntfs_free(buffer_le); ++ ++ return err; ++ ++dirty_vol: ++ log->set_dirty = true; ++ goto out; ++} ++ ++/* ++ * log_replay ++ * ++ * this function is called during mount operation ++ * it replays log and empties it ++ * initialized is set false if logfile contains '-1' ++ */ ++int log_replay(struct ntfs_inode *ni, bool *initialized) ++{ ++ int err; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ntfs_log *log; ++ ++ struct restart_info rst_info, rst_info2; ++ u64 rec_lsn, ra_lsn, checkpt_lsn = 0, rlsn = 0; ++ struct ATTR_NAME_ENTRY *attr_names = NULL; ++ struct ATTR_NAME_ENTRY *ane; ++ struct RESTART_TABLE *dptbl = NULL; ++ struct RESTART_TABLE *trtbl = NULL; ++ const struct RESTART_TABLE *rt; ++ struct RESTART_TABLE *oatbl = NULL; ++ struct inode *inode; ++ struct OpenAttr *oa; ++ struct ntfs_inode *ni_oe; ++ struct ATTRIB *attr = NULL; ++ u64 size, vcn, undo_next_lsn; ++ CLST rno, lcn, lcn0, len0, clen; ++ void *data; ++ struct NTFS_RESTART *rst = NULL; ++ struct lcb *lcb = NULL; ++ struct OPEN_ATTR_ENRTY *oe; ++ struct TRANSACTION_ENTRY *tr; ++ struct DIR_PAGE_ENTRY *dp; ++ u32 i, bytes_per_attr_entry; ++ u32 l_size = ni->vfs_inode.i_size; ++ u32 orig_file_size = l_size; ++ u32 page_size, vbo, tail, off, dlen; ++ u32 saved_len, rec_len, transact_id; ++ bool use_second_page; ++ struct RESTART_AREA *ra2, *ra = NULL; ++ struct CLIENT_REC *ca, *cr; ++ __le16 client; ++ struct RESTART_HDR *rh; ++ const struct LFS_RECORD_HDR *frh; ++ const struct LOG_REC_HDR *lrh; ++ bool is_mapped; ++ bool is_ro = sb_rdonly(sbi->sb); ++ u64 t64; ++ u16 t16; ++ u32 t32; ++ ++ /* Get the size of page. NOTE: To replay we can use default page */ ++#if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2 ++ page_size = norm_file_page(PAGE_SIZE, &l_size, true); ++#else ++ page_size = norm_file_page(PAGE_SIZE, &l_size, false); ++#endif ++ if (!page_size) ++ return -EINVAL; ++ ++ log = ntfs_zalloc(sizeof(struct ntfs_log)); ++ if (!log) ++ return -ENOMEM; ++ ++ log->ni = ni; ++ log->l_size = l_size; ++ log->one_page_buf = ntfs_malloc(page_size); ++ ++ if (!log->one_page_buf) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ log->page_size = page_size; ++ log->page_mask = page_size - 1; ++ log->page_bits = blksize_bits(page_size); ++ ++ /* Look for a restart area on the disk */ ++ err = log_read_rst(log, l_size, true, &rst_info); ++ if (err) ++ goto out; ++ ++ /* remember 'initialized' */ ++ *initialized = rst_info.initialized; ++ ++ if (!rst_info.restart) { ++ if (rst_info.initialized) { ++ /* no restart area but the file is not initialized */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ log_init_pg_hdr(log, page_size, page_size, 1, 1); ++ log_create(log, l_size, 0, get_random_int(), false, false); ++ ++ log->ra = ra; ++ ++ ra = log_create_ra(log); ++ if (!ra) { ++ err = -ENOMEM; ++ goto out; ++ } ++ log->ra = ra; ++ log->init_ra = true; ++ ++ goto process_log; ++ } ++ ++ /* ++ * If the restart offset above wasn't zero then we won't ++ * look for a second restart ++ */ ++ if (rst_info.vbo) ++ goto check_restart_area; ++ ++ err = log_read_rst(log, l_size, false, &rst_info2); ++ ++ /* Determine which restart area to use */ ++ if (!rst_info2.restart || rst_info2.last_lsn <= rst_info.last_lsn) ++ goto use_first_page; ++ ++ use_second_page = true; ++ ++ if (rst_info.chkdsk_was_run && page_size != rst_info.vbo) { ++ struct RECORD_PAGE_HDR *sp = NULL; ++ bool usa_error; ++ ++ if (!read_log_page(log, page_size, &sp, &usa_error) && ++ sp->rhdr.sign == NTFS_CHKD_SIGNATURE) { ++ use_second_page = false; ++ } ++ ntfs_free(sp); ++ } ++ ++ if (use_second_page) { ++ ntfs_free(rst_info.r_page); ++ memcpy(&rst_info, &rst_info2, sizeof(struct restart_info)); ++ rst_info2.r_page = NULL; ++ } ++ ++use_first_page: ++ ntfs_free(rst_info2.r_page); ++ ++check_restart_area: ++ /* If the restart area is at offset 0, we want to write the second restart area first */ ++ log->init_ra = !!rst_info.vbo; ++ ++ /* If we have a valid page then grab a pointer to the restart area */ ++ ra2 = rst_info.valid_page ++ ? Add2Ptr(rst_info.r_page, ++ le16_to_cpu(rst_info.r_page->ra_off)) ++ : NULL; ++ ++ if (rst_info.chkdsk_was_run || ++ (ra2 && ra2->client_idx[1] == LFS_NO_CLIENT_LE)) { ++ bool wrapped = false; ++ bool use_multi_page = false; ++ u32 open_log_count; ++ ++ /* Do some checks based on whether we have a valid log page */ ++ if (!rst_info.valid_page) { ++ open_log_count = get_random_int(); ++ goto init_log_instance; ++ } ++ open_log_count = le32_to_cpu(ra2->open_log_count); ++ ++ /* ++ * If the restart page size isn't changing then we want to ++ * check how much work we need to do ++ */ ++ if (page_size != le32_to_cpu(rst_info.r_page->sys_page_size)) ++ goto init_log_instance; ++ ++init_log_instance: ++ log_init_pg_hdr(log, page_size, page_size, 1, 1); ++ ++ log_create(log, l_size, rst_info.last_lsn, open_log_count, ++ wrapped, use_multi_page); ++ ++ ra = log_create_ra(log); ++ if (!ra) { ++ err = -ENOMEM; ++ goto out; ++ } ++ log->ra = ra; ++ ++ /* Put the restart areas and initialize the log file as required */ ++ goto process_log; ++ } ++ ++ if (!ra2) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * If the log page or the system page sizes have changed, we can't use the log file ++ * We must use the system page size instead of the default size ++ * if there is not a clean shutdown ++ */ ++ t32 = le32_to_cpu(rst_info.r_page->sys_page_size); ++ if (page_size != t32) { ++ l_size = orig_file_size; ++ page_size = ++ norm_file_page(t32, &l_size, t32 == DefaultLogPageSize); ++ } ++ ++ if (page_size != t32 || ++ page_size != le32_to_cpu(rst_info.r_page->page_size)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* If the file size has shrunk then we won't mount it */ ++ if (l_size < le64_to_cpu(ra2->l_size)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ log_init_pg_hdr(log, page_size, page_size, ++ le16_to_cpu(rst_info.r_page->major_ver), ++ le16_to_cpu(rst_info.r_page->minor_ver)); ++ ++ log->l_size = le64_to_cpu(ra2->l_size); ++ log->seq_num_bits = le32_to_cpu(ra2->seq_num_bits); ++ log->file_data_bits = sizeof(u64) * 8 - log->seq_num_bits; ++ log->seq_num_mask = (8 << log->file_data_bits) - 1; ++ log->last_lsn = le64_to_cpu(ra2->current_lsn); ++ log->seq_num = log->last_lsn >> log->file_data_bits; ++ log->ra_off = le16_to_cpu(rst_info.r_page->ra_off); ++ log->restart_size = log->sys_page_size - log->ra_off; ++ log->record_header_len = le16_to_cpu(ra2->rec_hdr_len); ++ log->ra_size = le16_to_cpu(ra2->ra_len); ++ log->data_off = le16_to_cpu(ra2->data_off); ++ log->data_size = log->page_size - log->data_off; ++ log->reserved = log->data_size - log->record_header_len; ++ ++ vbo = lsn_to_vbo(log, log->last_lsn); ++ ++ if (vbo < log->first_page) { ++ /* This is a pseudo lsn */ ++ log->l_flags |= NTFSLOG_NO_LAST_LSN; ++ log->next_page = log->first_page; ++ goto find_oldest; ++ } ++ ++ /* Find the end of this log record */ ++ off = final_log_off(log, log->last_lsn, ++ le32_to_cpu(ra2->last_lsn_data_len)); ++ ++ /* If we wrapped the file then increment the sequence number */ ++ if (off <= vbo) { ++ log->seq_num += 1; ++ log->l_flags |= NTFSLOG_WRAPPED; ++ } ++ ++ /* Now compute the next log page to use */ ++ vbo &= ~log->sys_page_mask; ++ tail = log->page_size - (off & log->page_mask) - 1; ++ ++ /* If we can fit another log record on the page, move back a page the log file */ ++ if (tail >= log->record_header_len) { ++ log->l_flags |= NTFSLOG_REUSE_TAIL; ++ log->next_page = vbo; ++ } else { ++ log->next_page = next_page_off(log, vbo); ++ } ++ ++find_oldest: ++ /* Find the oldest client lsn. Use the last flushed lsn as a starting point */ ++ log->oldest_lsn = log->last_lsn; ++ oldest_client_lsn(Add2Ptr(ra2, le16_to_cpu(ra2->client_off)), ++ ra2->client_idx[1], &log->oldest_lsn); ++ log->oldest_lsn_off = lsn_to_vbo(log, log->oldest_lsn); ++ ++ if (log->oldest_lsn_off < log->first_page) ++ log->l_flags |= NTFSLOG_NO_OLDEST_LSN; ++ ++ if (!(ra2->flags & RESTART_SINGLE_PAGE_IO)) ++ log->l_flags |= NTFSLOG_WRAPPED | NTFSLOG_MULTIPLE_PAGE_IO; ++ ++ log->current_openlog_count = le32_to_cpu(ra2->open_log_count); ++ log->total_avail_pages = log->l_size - log->first_page; ++ log->total_avail = log->total_avail_pages >> log->page_bits; ++ log->max_current_avail = log->total_avail * log->reserved; ++ log->total_avail = log->total_avail * log->data_size; ++ ++ log->current_avail = current_log_avail(log); ++ ++ ra = ntfs_zalloc(log->restart_size); ++ if (!ra) { ++ err = -ENOMEM; ++ goto out; ++ } ++ log->ra = ra; ++ ++ t16 = le16_to_cpu(ra2->client_off); ++ if (t16 == offsetof(struct RESTART_AREA, clients)) { ++ memcpy(ra, ra2, log->ra_size); ++ } else { ++ memcpy(ra, ra2, offsetof(struct RESTART_AREA, clients)); ++ memcpy(ra->clients, Add2Ptr(ra2, t16), ++ le16_to_cpu(ra2->ra_len) - t16); ++ ++ log->current_openlog_count = get_random_int(); ++ ra->open_log_count = cpu_to_le32(log->current_openlog_count); ++ log->ra_size = offsetof(struct RESTART_AREA, clients) + ++ sizeof(struct CLIENT_REC); ++ ra->client_off = ++ cpu_to_le16(offsetof(struct RESTART_AREA, clients)); ++ ra->ra_len = cpu_to_le16(log->ra_size); ++ } ++ ++ le32_add_cpu(&ra->open_log_count, 1); ++ ++ /* Now we need to walk through looking for the last lsn */ ++ err = last_log_lsn(log); ++ if (err) ++ goto out; ++ ++ log->current_avail = current_log_avail(log); ++ ++ /* Remember which restart area to write first */ ++ log->init_ra = rst_info.vbo; ++ ++process_log: ++ /* 1.0, 1.1, 2.0 log->major_ver/minor_ver - short values */ ++ switch ((log->major_ver << 16) + log->minor_ver) { ++ case 0x10000: ++ case 0x10001: ++ case 0x20000: ++ break; ++ default: ++ ntfs_warn(sbi->sb, "\x24LogFile version %d.%d is not supported", ++ log->major_ver, log->minor_ver); ++ err = -EOPNOTSUPP; ++ log->set_dirty = true; ++ goto out; ++ } ++ ++ /* One client "NTFS" per logfile */ ++ ca = Add2Ptr(ra, le16_to_cpu(ra->client_off)); ++ ++ for (client = ra->client_idx[1];; client = cr->next_client) { ++ if (client == LFS_NO_CLIENT_LE) { ++ /* Insert "NTFS" client LogFile */ ++ client = ra->client_idx[0]; ++ if (client == LFS_NO_CLIENT_LE) ++ return -EINVAL; ++ ++ t16 = le16_to_cpu(client); ++ cr = ca + t16; ++ ++ remove_client(ca, cr, &ra->client_idx[0]); ++ ++ cr->restart_lsn = 0; ++ cr->oldest_lsn = cpu_to_le64(log->oldest_lsn); ++ cr->name_bytes = cpu_to_le32(8); ++ cr->name[0] = cpu_to_le16('N'); ++ cr->name[1] = cpu_to_le16('T'); ++ cr->name[2] = cpu_to_le16('F'); ++ cr->name[3] = cpu_to_le16('S'); ++ ++ add_client(ca, t16, &ra->client_idx[1]); ++ break; ++ } ++ ++ cr = ca + le16_to_cpu(client); ++ ++ if (cpu_to_le32(8) == cr->name_bytes && ++ cpu_to_le16('N') == cr->name[0] && ++ cpu_to_le16('T') == cr->name[1] && ++ cpu_to_le16('F') == cr->name[2] && ++ cpu_to_le16('S') == cr->name[3]) ++ break; ++ } ++ ++ /* Update the client handle with the client block information */ ++ log->client_id.seq_num = cr->seq_num; ++ log->client_id.client_idx = client; ++ ++ err = read_rst_area(log, &rst, &ra_lsn); ++ if (err) ++ goto out; ++ ++ if (!rst) ++ goto out; ++ ++ bytes_per_attr_entry = !rst->major_ver ? 0x2C : 0x28; ++ ++ checkpt_lsn = le64_to_cpu(rst->check_point_start); ++ if (!checkpt_lsn) ++ checkpt_lsn = ra_lsn; ++ ++ /* Allocate and Read the Transaction Table */ ++ if (!rst->transact_table_len) ++ goto check_dirty_page_table; ++ ++ t64 = le64_to_cpu(rst->transact_table_lsn); ++ err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); ++ if (err) ++ goto out; ++ ++ lrh = lcb->log_rec; ++ frh = lcb->lrh; ++ rec_len = le32_to_cpu(frh->client_data_len); ++ ++ if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id), ++ bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ t16 = le16_to_cpu(lrh->redo_off); ++ ++ rt = Add2Ptr(lrh, t16); ++ t32 = rec_len - t16; ++ ++ /* Now check that this is a valid restart table */ ++ if (!check_rstbl(rt, t32)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ trtbl = ntfs_memdup(rt, t32); ++ if (!trtbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ lcb_put(lcb); ++ lcb = NULL; ++ ++check_dirty_page_table: ++ /* The next record back should be the Dirty Pages Table */ ++ if (!rst->dirty_pages_len) ++ goto check_attribute_names; ++ ++ t64 = le64_to_cpu(rst->dirty_pages_table_lsn); ++ err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); ++ if (err) ++ goto out; ++ ++ lrh = lcb->log_rec; ++ frh = lcb->lrh; ++ rec_len = le32_to_cpu(frh->client_data_len); ++ ++ if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id), ++ bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ t16 = le16_to_cpu(lrh->redo_off); ++ ++ rt = Add2Ptr(lrh, t16); ++ t32 = rec_len - t16; ++ ++ /* Now check that this is a valid restart table */ ++ if (!check_rstbl(rt, t32)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ dptbl = ntfs_memdup(rt, t32); ++ if (!dptbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Convert Ra version '0' into version '1' */ ++ if (rst->major_ver) ++ goto end_conv_1; ++ ++ dp = NULL; ++ while ((dp = enum_rstbl(dptbl, dp))) { ++ struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp; ++ // NOTE: Danger. Check for of boundary ++ memmove(&dp->vcn, &dp0->vcn_low, ++ 2 * sizeof(u64) + ++ le32_to_cpu(dp->lcns_follow) * sizeof(u64)); ++ } ++ ++end_conv_1: ++ lcb_put(lcb); ++ lcb = NULL; ++ ++ /* Go through the table and remove the duplicates, remembering the oldest lsn values */ ++ if (sbi->cluster_size <= log->page_size) ++ goto trace_dp_table; ++ ++ dp = NULL; ++ while ((dp = enum_rstbl(dptbl, dp))) { ++ struct DIR_PAGE_ENTRY *next = dp; ++ ++ while ((next = enum_rstbl(dptbl, next))) { ++ if (next->target_attr == dp->target_attr && ++ next->vcn == dp->vcn) { ++ if (le64_to_cpu(next->oldest_lsn) < ++ le64_to_cpu(dp->oldest_lsn)) { ++ dp->oldest_lsn = next->oldest_lsn; ++ } ++ ++ free_rsttbl_idx(dptbl, PtrOffset(dptbl, next)); ++ } ++ } ++ } ++trace_dp_table: ++check_attribute_names: ++ /* The next record should be the Attribute Names */ ++ if (!rst->attr_names_len) ++ goto check_attr_table; ++ ++ t64 = le64_to_cpu(rst->attr_names_lsn); ++ err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); ++ if (err) ++ goto out; ++ ++ lrh = lcb->log_rec; ++ frh = lcb->lrh; ++ rec_len = le32_to_cpu(frh->client_data_len); ++ ++ if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id), ++ bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ t32 = lrh_length(lrh); ++ rec_len -= t32; ++ ++ attr_names = ntfs_memdup(Add2Ptr(lrh, t32), rec_len); ++ ++ lcb_put(lcb); ++ lcb = NULL; ++ ++check_attr_table: ++ /* The next record should be the attribute Table */ ++ if (!rst->open_attr_len) ++ goto check_attribute_names2; ++ ++ t64 = le64_to_cpu(rst->open_attr_table_lsn); ++ err = read_log_rec_lcb(log, t64, lcb_ctx_prev, &lcb); ++ if (err) ++ goto out; ++ ++ lrh = lcb->log_rec; ++ frh = lcb->lrh; ++ rec_len = le32_to_cpu(frh->client_data_len); ++ ++ if (!check_log_rec(lrh, rec_len, le32_to_cpu(frh->transact_id), ++ bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ t16 = le16_to_cpu(lrh->redo_off); ++ ++ rt = Add2Ptr(lrh, t16); ++ t32 = rec_len - t16; ++ ++ if (!check_rstbl(rt, t32)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ oatbl = ntfs_memdup(rt, t32); ++ if (!oatbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ log->open_attr_tbl = oatbl; ++ ++ /* Clear all of the Attr pointers */ ++ oe = NULL; ++ while ((oe = enum_rstbl(oatbl, oe))) { ++ if (!rst->major_ver) { ++ struct OPEN_ATTR_ENRTY_32 oe0; ++ ++ /* Really 'oe' points to OPEN_ATTR_ENRTY_32 */ ++ memcpy(&oe0, oe, SIZEOF_OPENATTRIBUTEENTRY0); ++ ++ oe->bytes_per_index = oe0.bytes_per_index; ++ oe->type = oe0.type; ++ oe->is_dirty_pages = oe0.is_dirty_pages; ++ oe->name_len = 0; ++ oe->ref = oe0.ref; ++ oe->open_record_lsn = oe0.open_record_lsn; ++ } ++ ++ oe->is_attr_name = 0; ++ oe->ptr = NULL; ++ } ++ ++ lcb_put(lcb); ++ lcb = NULL; ++ ++check_attribute_names2: ++ if (!rst->attr_names_len) ++ goto trace_attribute_table; ++ ++ ane = attr_names; ++ if (!oatbl) ++ goto trace_attribute_table; ++ while (ane->off) { ++ /* TODO: Clear table on exit! */ ++ oe = Add2Ptr(oatbl, le16_to_cpu(ane->off)); ++ t16 = le16_to_cpu(ane->name_bytes); ++ oe->name_len = t16 / sizeof(short); ++ oe->ptr = ane->name; ++ oe->is_attr_name = 2; ++ ane = Add2Ptr(ane, sizeof(struct ATTR_NAME_ENTRY) + t16); ++ } ++ ++trace_attribute_table: ++ /* ++ * If the checkpt_lsn is zero, then this is a freshly ++ * formatted disk and we have no work to do ++ */ ++ if (!checkpt_lsn) { ++ err = 0; ++ goto out; ++ } ++ ++ if (!oatbl) { ++ oatbl = init_rsttbl(bytes_per_attr_entry, 8); ++ if (!oatbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ log->open_attr_tbl = oatbl; ++ ++ /* Start the analysis pass from the Checkpoint lsn. */ ++ rec_lsn = checkpt_lsn; ++ ++ /* Read the first lsn */ ++ err = read_log_rec_lcb(log, checkpt_lsn, lcb_ctx_next, &lcb); ++ if (err) ++ goto out; ++ ++ /* Loop to read all subsequent records to the end of the log file */ ++next_log_record_analyze: ++ err = read_next_log_rec(log, lcb, &rec_lsn); ++ if (err) ++ goto out; ++ ++ if (!rec_lsn) ++ goto end_log_records_enumerate; ++ ++ frh = lcb->lrh; ++ transact_id = le32_to_cpu(frh->transact_id); ++ rec_len = le32_to_cpu(frh->client_data_len); ++ lrh = lcb->log_rec; ++ ++ if (!check_log_rec(lrh, rec_len, transact_id, bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * The first lsn after the previous lsn remembered ++ * the checkpoint is the first candidate for the rlsn ++ */ ++ if (!rlsn) ++ rlsn = rec_lsn; ++ ++ if (LfsClientRecord != frh->record_type) ++ goto next_log_record_analyze; ++ ++ /* ++ * Now update the Transaction Table for this transaction ++ * If there is no entry present or it is unallocated we allocate the entry ++ */ ++ if (!trtbl) { ++ trtbl = init_rsttbl(sizeof(struct TRANSACTION_ENTRY), ++ INITIAL_NUMBER_TRANSACTIONS); ++ if (!trtbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ tr = Add2Ptr(trtbl, transact_id); ++ ++ if (transact_id >= bytes_per_rt(trtbl) || ++ tr->next != RESTART_ENTRY_ALLOCATED_LE) { ++ tr = alloc_rsttbl_from_idx(&trtbl, transact_id); ++ if (!tr) { ++ err = -ENOMEM; ++ goto out; ++ } ++ tr->transact_state = TransactionActive; ++ tr->first_lsn = cpu_to_le64(rec_lsn); ++ } ++ ++ tr->prev_lsn = tr->undo_next_lsn = cpu_to_le64(rec_lsn); ++ ++ /* ++ * If this is a compensation log record, then change ++ * the undo_next_lsn to be the undo_next_lsn of this record ++ */ ++ if (lrh->undo_op == cpu_to_le16(CompensationLogRecord)) ++ tr->undo_next_lsn = frh->client_undo_next_lsn; ++ ++ /* Dispatch to handle log record depending on type */ ++ switch (le16_to_cpu(lrh->redo_op)) { ++ case InitializeFileRecordSegment: ++ case DeallocateFileRecordSegment: ++ case WriteEndOfFileRecordSegment: ++ case CreateAttribute: ++ case DeleteAttribute: ++ case UpdateResidentValue: ++ case UpdateNonresidentValue: ++ case UpdateMappingPairs: ++ case SetNewAttributeSizes: ++ case AddIndexEntryRoot: ++ case DeleteIndexEntryRoot: ++ case AddIndexEntryAllocation: ++ case DeleteIndexEntryAllocation: ++ case WriteEndOfIndexBuffer: ++ case SetIndexEntryVcnRoot: ++ case SetIndexEntryVcnAllocation: ++ case UpdateFileNameRoot: ++ case UpdateFileNameAllocation: ++ case SetBitsInNonresidentBitMap: ++ case ClearBitsInNonresidentBitMap: ++ case UpdateRecordDataRoot: ++ case UpdateRecordDataAllocation: ++ case ZeroEndOfFileRecord: ++ t16 = le16_to_cpu(lrh->target_attr); ++ t64 = le64_to_cpu(lrh->target_vcn); ++ dp = find_dp(dptbl, t16, t64); ++ ++ if (dp) ++ goto copy_lcns; ++ ++ /* ++ * Calculate the number of clusters per page the system ++ * which wrote the checkpoint, possibly creating the table ++ */ ++ if (dptbl) { ++ t32 = (le16_to_cpu(dptbl->size) - ++ sizeof(struct DIR_PAGE_ENTRY)) / ++ sizeof(u64); ++ } else { ++ t32 = log->clst_per_page; ++ ntfs_free(dptbl); ++ dptbl = init_rsttbl(struct_size(dp, page_lcns, t32), ++ 32); ++ if (!dptbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ dp = alloc_rsttbl_idx(&dptbl); ++ dp->target_attr = cpu_to_le32(t16); ++ dp->transfer_len = cpu_to_le32(t32 << sbi->cluster_bits); ++ dp->lcns_follow = cpu_to_le32(t32); ++ dp->vcn = cpu_to_le64(t64 & ~((u64)t32 - 1)); ++ dp->oldest_lsn = cpu_to_le64(rec_lsn); ++ ++copy_lcns: ++ /* ++ * Copy the Lcns from the log record into the Dirty Page Entry ++ * TODO: for different page size support, must somehow make ++ * whole routine a loop, case Lcns do not fit below ++ */ ++ t16 = le16_to_cpu(lrh->lcns_follow); ++ for (i = 0; i < t16; i++) { ++ size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - ++ le64_to_cpu(dp->vcn)); ++ dp->page_lcns[j + i] = lrh->page_lcns[i]; ++ } ++ ++ goto next_log_record_analyze; ++ ++ case DeleteDirtyClusters: { ++ u32 range_count = ++ le16_to_cpu(lrh->redo_len) / sizeof(struct LCN_RANGE); ++ const struct LCN_RANGE *r = ++ Add2Ptr(lrh, le16_to_cpu(lrh->redo_off)); ++ ++ /* Loop through all of the Lcn ranges this log record */ ++ for (i = 0; i < range_count; i++, r++) { ++ u64 lcn0 = le64_to_cpu(r->lcn); ++ u64 lcn_e = lcn0 + le64_to_cpu(r->len) - 1; ++ ++ dp = NULL; ++ while ((dp = enum_rstbl(dptbl, dp))) { ++ u32 j; ++ ++ t32 = le32_to_cpu(dp->lcns_follow); ++ for (j = 0; j < t32; j++) { ++ t64 = le64_to_cpu(dp->page_lcns[j]); ++ if (t64 >= lcn0 && t64 <= lcn_e) ++ dp->page_lcns[j] = 0; ++ } ++ } ++ } ++ goto next_log_record_analyze; ++ ; ++ } ++ ++ case OpenNonresidentAttribute: ++ t16 = le16_to_cpu(lrh->target_attr); ++ if (t16 >= bytes_per_rt(oatbl)) { ++ /* ++ * Compute how big the table needs to be. ++ * Add 10 extra entries for some cushion ++ */ ++ u32 new_e = t16 / le16_to_cpu(oatbl->size); ++ ++ new_e += 10 - le16_to_cpu(oatbl->used); ++ ++ oatbl = extend_rsttbl(oatbl, new_e, ~0u); ++ log->open_attr_tbl = oatbl; ++ if (!oatbl) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } ++ ++ /* Point to the entry being opened */ ++ oe = alloc_rsttbl_from_idx(&oatbl, t16); ++ log->open_attr_tbl = oatbl; ++ if (!oe) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Initialize this entry from the log record */ ++ t16 = le16_to_cpu(lrh->redo_off); ++ if (!rst->major_ver) { ++ /* Convert version '0' into version '1' */ ++ struct OPEN_ATTR_ENRTY_32 *oe0 = Add2Ptr(lrh, t16); ++ ++ oe->bytes_per_index = oe0->bytes_per_index; ++ oe->type = oe0->type; ++ oe->is_dirty_pages = oe0->is_dirty_pages; ++ oe->name_len = 0; //oe0.name_len; ++ oe->ref = oe0->ref; ++ oe->open_record_lsn = oe0->open_record_lsn; ++ } else { ++ memcpy(oe, Add2Ptr(lrh, t16), bytes_per_attr_entry); ++ } ++ ++ t16 = le16_to_cpu(lrh->undo_len); ++ if (t16) { ++ oe->ptr = ntfs_malloc(t16); ++ if (!oe->ptr) { ++ err = -ENOMEM; ++ goto out; ++ } ++ oe->name_len = t16 / sizeof(short); ++ memcpy(oe->ptr, ++ Add2Ptr(lrh, le16_to_cpu(lrh->undo_off)), t16); ++ oe->is_attr_name = 1; ++ } else { ++ oe->ptr = NULL; ++ oe->is_attr_name = 0; ++ } ++ ++ goto next_log_record_analyze; ++ ++ case HotFix: ++ t16 = le16_to_cpu(lrh->target_attr); ++ t64 = le64_to_cpu(lrh->target_vcn); ++ dp = find_dp(dptbl, t16, t64); ++ if (dp) { ++ size_t j = le64_to_cpu(lrh->target_vcn) - ++ le64_to_cpu(dp->vcn); ++ if (dp->page_lcns[j]) ++ dp->page_lcns[j] = lrh->page_lcns[0]; ++ } ++ goto next_log_record_analyze; ++ ++ case EndTopLevelAction: ++ tr = Add2Ptr(trtbl, transact_id); ++ tr->prev_lsn = cpu_to_le64(rec_lsn); ++ tr->undo_next_lsn = frh->client_undo_next_lsn; ++ goto next_log_record_analyze; ++ ++ case PrepareTransaction: ++ tr = Add2Ptr(trtbl, transact_id); ++ tr->transact_state = TransactionPrepared; ++ goto next_log_record_analyze; ++ ++ case CommitTransaction: ++ tr = Add2Ptr(trtbl, transact_id); ++ tr->transact_state = TransactionCommitted; ++ goto next_log_record_analyze; ++ ++ case ForgetTransaction: ++ free_rsttbl_idx(trtbl, transact_id); ++ goto next_log_record_analyze; ++ ++ case Noop: ++ case OpenAttributeTableDump: ++ case AttributeNamesDump: ++ case DirtyPageTableDump: ++ case TransactionTableDump: ++ /* The following cases require no action the Analysis Pass */ ++ goto next_log_record_analyze; ++ ++ default: ++ /* ++ * All codes will be explicitly handled. ++ * If we see a code we do not expect, then we are trouble ++ */ ++ goto next_log_record_analyze; ++ } ++ ++end_log_records_enumerate: ++ lcb_put(lcb); ++ lcb = NULL; ++ ++ /* ++ * Scan the Dirty Page Table and Transaction Table for ++ * the lowest lsn, and return it as the Redo lsn ++ */ ++ dp = NULL; ++ while ((dp = enum_rstbl(dptbl, dp))) { ++ t64 = le64_to_cpu(dp->oldest_lsn); ++ if (t64 && t64 < rlsn) ++ rlsn = t64; ++ } ++ ++ tr = NULL; ++ while ((tr = enum_rstbl(trtbl, tr))) { ++ t64 = le64_to_cpu(tr->first_lsn); ++ if (t64 && t64 < rlsn) ++ rlsn = t64; ++ } ++ ++ /* Only proceed if the Dirty Page Table or Transaction table are not empty */ ++ if ((!dptbl || !dptbl->total) && (!trtbl || !trtbl->total)) ++ goto end_reply; ++ ++ sbi->flags |= NTFS_FLAGS_NEED_REPLAY; ++ if (is_ro) ++ goto out; ++ ++ /* Reopen all of the attributes with dirty pages */ ++ oe = NULL; ++next_open_attribute: ++ ++ oe = enum_rstbl(oatbl, oe); ++ if (!oe) { ++ err = 0; ++ dp = NULL; ++ goto next_dirty_page; ++ } ++ ++ oa = ntfs_zalloc(sizeof(struct OpenAttr)); ++ if (!oa) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ inode = ntfs_iget5(sbi->sb, &oe->ref, NULL); ++ if (IS_ERR(inode)) ++ goto fake_attr; ++ ++ if (is_bad_inode(inode)) { ++ iput(inode); ++fake_attr: ++ if (oa->ni) { ++ iput(&oa->ni->vfs_inode); ++ oa->ni = NULL; ++ } ++ ++ attr = attr_create_nonres_log(sbi, oe->type, 0, oe->ptr, ++ oe->name_len, 0); ++ if (!attr) { ++ ntfs_free(oa); ++ err = -ENOMEM; ++ goto out; ++ } ++ oa->attr = attr; ++ oa->run1 = &oa->run0; ++ goto final_oe; ++ } ++ ++ ni_oe = ntfs_i(inode); ++ oa->ni = ni_oe; ++ ++ attr = ni_find_attr(ni_oe, NULL, NULL, oe->type, oe->ptr, oe->name_len, ++ NULL, NULL); ++ ++ if (!attr) ++ goto fake_attr; ++ ++ t32 = le32_to_cpu(attr->size); ++ oa->attr = ntfs_memdup(attr, t32); ++ if (!oa->attr) ++ goto fake_attr; ++ ++ if (!S_ISDIR(inode->i_mode)) { ++ if (attr->type == ATTR_DATA && !attr->name_len) { ++ oa->run1 = &ni_oe->file.run; ++ goto final_oe; ++ } ++ } else { ++ if (attr->type == ATTR_ALLOC && ++ attr->name_len == ARRAY_SIZE(I30_NAME) && ++ !memcmp(attr_name(attr), I30_NAME, sizeof(I30_NAME))) { ++ oa->run1 = &ni_oe->dir.alloc_run; ++ goto final_oe; ++ } ++ } ++ ++ if (attr->non_res) { ++ u16 roff = le16_to_cpu(attr->nres.run_off); ++ CLST svcn = le64_to_cpu(attr->nres.svcn); ++ ++ err = run_unpack(&oa->run0, sbi, inode->i_ino, svcn, ++ le64_to_cpu(attr->nres.evcn), svcn, ++ Add2Ptr(attr, roff), t32 - roff); ++ if (err < 0) { ++ ntfs_free(oa->attr); ++ oa->attr = NULL; ++ goto fake_attr; ++ } ++ err = 0; ++ } ++ oa->run1 = &oa->run0; ++ attr = oa->attr; ++ ++final_oe: ++ if (oe->is_attr_name == 1) ++ ntfs_free(oe->ptr); ++ oe->is_attr_name = 0; ++ oe->ptr = oa; ++ oe->name_len = attr->name_len; ++ ++ goto next_open_attribute; ++ ++ /* ++ * Now loop through the dirty page table to extract all of the Vcn/Lcn ++ * Mapping that we have, and insert it into the appropriate run ++ */ ++next_dirty_page: ++ dp = enum_rstbl(dptbl, dp); ++ if (!dp) ++ goto do_redo_1; ++ ++ oe = Add2Ptr(oatbl, le32_to_cpu(dp->target_attr)); ++ ++ if (oe->next != RESTART_ENTRY_ALLOCATED_LE) ++ goto next_dirty_page; ++ ++ oa = oe->ptr; ++ if (!oa) ++ goto next_dirty_page; ++ ++ i = -1; ++next_dirty_page_vcn: ++ i += 1; ++ if (i >= le32_to_cpu(dp->lcns_follow)) ++ goto next_dirty_page; ++ ++ vcn = le64_to_cpu(dp->vcn) + i; ++ size = (vcn + 1) << sbi->cluster_bits; ++ ++ if (!dp->page_lcns[i]) ++ goto next_dirty_page_vcn; ++ ++ rno = ino_get(&oe->ref); ++ if (rno <= MFT_REC_MIRR && ++ size < (MFT_REC_VOL + 1) * sbi->record_size && ++ oe->type == ATTR_DATA) { ++ goto next_dirty_page_vcn; ++ } ++ ++ lcn = le64_to_cpu(dp->page_lcns[i]); ++ ++ if ((!run_lookup_entry(oa->run1, vcn, &lcn0, &len0, NULL) || ++ lcn0 != lcn) && ++ !run_add_entry(oa->run1, vcn, lcn, 1, false)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ attr = oa->attr; ++ t64 = le64_to_cpu(attr->nres.alloc_size); ++ if (size > t64) { ++ attr->nres.valid_size = attr->nres.data_size = ++ attr->nres.alloc_size = cpu_to_le64(size); ++ } ++ goto next_dirty_page_vcn; ++ ++do_redo_1: ++ /* ++ * Perform the Redo Pass, to restore all of the dirty pages to the same ++ * contents that they had immediately before the crash ++ * If the dirty page table is empty, then we can skip the entire Redo Pass ++ */ ++ if (!dptbl || !dptbl->total) ++ goto do_undo_action; ++ ++ rec_lsn = rlsn; ++ ++ /* ++ * Read the record at the Redo lsn, before falling ++ * into common code to handle each record ++ */ ++ err = read_log_rec_lcb(log, rlsn, lcb_ctx_next, &lcb); ++ if (err) ++ goto out; ++ ++ /* ++ * Now loop to read all of our log records forwards, ++ * until we hit the end of the file, cleaning up at the end ++ */ ++do_action_next: ++ frh = lcb->lrh; ++ ++ if (LfsClientRecord != frh->record_type) ++ goto read_next_log_do_action; ++ ++ transact_id = le32_to_cpu(frh->transact_id); ++ rec_len = le32_to_cpu(frh->client_data_len); ++ lrh = lcb->log_rec; ++ ++ if (!check_log_rec(lrh, rec_len, transact_id, bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Ignore log records that do not update pages */ ++ if (lrh->lcns_follow) ++ goto find_dirty_page; ++ ++ goto read_next_log_do_action; ++ ++find_dirty_page: ++ t16 = le16_to_cpu(lrh->target_attr); ++ t64 = le64_to_cpu(lrh->target_vcn); ++ dp = find_dp(dptbl, t16, t64); ++ ++ if (!dp) ++ goto read_next_log_do_action; ++ ++ if (rec_lsn < le64_to_cpu(dp->oldest_lsn)) ++ goto read_next_log_do_action; ++ ++ t16 = le16_to_cpu(lrh->target_attr); ++ if (t16 >= bytes_per_rt(oatbl)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ oe = Add2Ptr(oatbl, t16); ++ ++ if (oe->next != RESTART_ENTRY_ALLOCATED_LE) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ oa = oe->ptr; ++ ++ if (!oa) { ++ err = -EINVAL; ++ goto out; ++ } ++ attr = oa->attr; ++ ++ vcn = le64_to_cpu(lrh->target_vcn); ++ ++ if (!run_lookup_entry(oa->run1, vcn, &lcn, NULL, NULL) || ++ lcn == SPARSE_LCN) { ++ goto read_next_log_do_action; ++ } ++ ++ /* Point to the Redo data and get its length */ ++ data = Add2Ptr(lrh, le16_to_cpu(lrh->redo_off)); ++ dlen = le16_to_cpu(lrh->redo_len); ++ ++ /* Shorten length by any Lcns which were deleted */ ++ saved_len = dlen; ++ ++ for (i = le16_to_cpu(lrh->lcns_follow); i; i--) { ++ size_t j; ++ u32 alen, voff; ++ ++ voff = le16_to_cpu(lrh->record_off) + ++ le16_to_cpu(lrh->attr_off); ++ voff += le16_to_cpu(lrh->cluster_off) << SECTOR_SHIFT; ++ ++ /* If the Vcn question is allocated, we can just get out.*/ ++ j = le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn); ++ if (dp->page_lcns[j + i - 1]) ++ break; ++ ++ if (!saved_len) ++ saved_len = 1; ++ ++ /* ++ * Calculate the allocated space left relative to the ++ * log record Vcn, after removing this unallocated Vcn ++ */ ++ alen = (i - 1) << sbi->cluster_bits; ++ ++ /* ++ * If the update described this log record goes beyond ++ * the allocated space, then we will have to reduce the length ++ */ ++ if (voff >= alen) ++ dlen = 0; ++ else if (voff + dlen > alen) ++ dlen = alen - voff; ++ } ++ ++ /* If the resulting dlen from above is now zero, we can skip this log record */ ++ if (!dlen && saved_len) ++ goto read_next_log_do_action; ++ ++ t16 = le16_to_cpu(lrh->redo_op); ++ if (can_skip_action(t16)) ++ goto read_next_log_do_action; ++ ++ /* Apply the Redo operation a common routine */ ++ err = do_action(log, oe, lrh, t16, data, dlen, rec_len, &rec_lsn); ++ if (err) ++ goto out; ++ ++ /* Keep reading and looping back until end of file */ ++read_next_log_do_action: ++ err = read_next_log_rec(log, lcb, &rec_lsn); ++ if (!err && rec_lsn) ++ goto do_action_next; ++ ++ lcb_put(lcb); ++ lcb = NULL; ++ ++do_undo_action: ++ /* Scan Transaction Table */ ++ tr = NULL; ++transaction_table_next: ++ tr = enum_rstbl(trtbl, tr); ++ if (!tr) ++ goto undo_action_done; ++ ++ if (TransactionActive != tr->transact_state || !tr->undo_next_lsn) { ++ free_rsttbl_idx(trtbl, PtrOffset(trtbl, tr)); ++ goto transaction_table_next; ++ } ++ ++ log->transaction_id = PtrOffset(trtbl, tr); ++ undo_next_lsn = le64_to_cpu(tr->undo_next_lsn); ++ ++ /* ++ * We only have to do anything if the transaction has ++ * something its undo_next_lsn field ++ */ ++ if (!undo_next_lsn) ++ goto commit_undo; ++ ++ /* Read the first record to be undone by this transaction */ ++ err = read_log_rec_lcb(log, undo_next_lsn, lcb_ctx_undo_next, &lcb); ++ if (err) ++ goto out; ++ ++ /* ++ * Now loop to read all of our log records forwards, ++ * until we hit the end of the file, cleaning up at the end ++ */ ++undo_action_next: ++ ++ lrh = lcb->log_rec; ++ frh = lcb->lrh; ++ transact_id = le32_to_cpu(frh->transact_id); ++ rec_len = le32_to_cpu(frh->client_data_len); ++ ++ if (!check_log_rec(lrh, rec_len, transact_id, bytes_per_attr_entry)) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (lrh->undo_op == cpu_to_le16(Noop)) ++ goto read_next_log_undo_action; ++ ++ oe = Add2Ptr(oatbl, le16_to_cpu(lrh->target_attr)); ++ oa = oe->ptr; ++ ++ t16 = le16_to_cpu(lrh->lcns_follow); ++ if (!t16) ++ goto add_allocated_vcns; ++ ++ is_mapped = run_lookup_entry(oa->run1, le64_to_cpu(lrh->target_vcn), ++ &lcn, &clen, NULL); ++ ++ /* ++ * If the mapping isn't already the table or the mapping ++ * corresponds to a hole the mapping, we need to make sure ++ * there is no partial page already memory ++ */ ++ if (is_mapped && lcn != SPARSE_LCN && clen >= t16) ++ goto add_allocated_vcns; ++ ++ vcn = le64_to_cpu(lrh->target_vcn); ++ vcn &= ~(log->clst_per_page - 1); ++ ++add_allocated_vcns: ++ for (i = 0, vcn = le64_to_cpu(lrh->target_vcn), ++ size = (vcn + 1) << sbi->cluster_bits; ++ i < t16; i++, vcn += 1, size += sbi->cluster_size) { ++ attr = oa->attr; ++ if (!attr->non_res) { ++ if (size > le32_to_cpu(attr->res.data_size)) ++ attr->res.data_size = cpu_to_le32(size); ++ } else { ++ if (size > le64_to_cpu(attr->nres.data_size)) ++ attr->nres.valid_size = attr->nres.data_size = ++ attr->nres.alloc_size = ++ cpu_to_le64(size); ++ } ++ } ++ ++ t16 = le16_to_cpu(lrh->undo_op); ++ if (can_skip_action(t16)) ++ goto read_next_log_undo_action; ++ ++ /* Point to the Redo data and get its length */ ++ data = Add2Ptr(lrh, le16_to_cpu(lrh->undo_off)); ++ dlen = le16_to_cpu(lrh->undo_len); ++ ++ /* it is time to apply the undo action */ ++ err = do_action(log, oe, lrh, t16, data, dlen, rec_len, NULL); ++ ++read_next_log_undo_action: ++ /* ++ * Keep reading and looping back until we have read the ++ * last record for this transaction ++ */ ++ err = read_next_log_rec(log, lcb, &rec_lsn); ++ if (err) ++ goto out; ++ ++ if (rec_lsn) ++ goto undo_action_next; ++ ++ lcb_put(lcb); ++ lcb = NULL; ++ ++commit_undo: ++ free_rsttbl_idx(trtbl, log->transaction_id); ++ ++ log->transaction_id = 0; ++ ++ goto transaction_table_next; ++ ++undo_action_done: ++ ++ ntfs_update_mftmirr(sbi, 0); ++ ++ sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY; ++ ++end_reply: ++ ++ err = 0; ++ if (is_ro) ++ goto out; ++ ++ rh = ntfs_zalloc(log->page_size); ++ if (!rh) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ rh->rhdr.sign = NTFS_RSTR_SIGNATURE; ++ rh->rhdr.fix_off = cpu_to_le16(offsetof(struct RESTART_HDR, fixups)); ++ t16 = (log->page_size >> SECTOR_SHIFT) + 1; ++ rh->rhdr.fix_num = cpu_to_le16(t16); ++ rh->sys_page_size = cpu_to_le32(log->page_size); ++ rh->page_size = cpu_to_le32(log->page_size); ++ ++ t16 = QuadAlign(offsetof(struct RESTART_HDR, fixups) + ++ sizeof(short) * t16); ++ rh->ra_off = cpu_to_le16(t16); ++ rh->minor_ver = cpu_to_le16(1); // 0x1A: ++ rh->major_ver = cpu_to_le16(1); // 0x1C: ++ ++ ra2 = Add2Ptr(rh, t16); ++ memcpy(ra2, ra, sizeof(struct RESTART_AREA)); ++ ++ ra2->client_idx[0] = 0; ++ ra2->client_idx[1] = LFS_NO_CLIENT_LE; ++ ra2->flags = cpu_to_le16(2); ++ ++ le32_add_cpu(&ra2->open_log_count, 1); ++ ++ ntfs_fix_pre_write(&rh->rhdr, log->page_size); ++ ++ err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rh, log->page_size); ++ if (!err) ++ err = ntfs_sb_write_run(sbi, &log->ni->file.run, log->page_size, ++ rh, log->page_size); ++ ++ ntfs_free(rh); ++ if (err) ++ goto out; ++ ++out: ++ ntfs_free(rst); ++ if (lcb) ++ lcb_put(lcb); ++ ++ /* Scan the Open Attribute Table to close all of the open attributes */ ++ oe = NULL; ++ while ((oe = enum_rstbl(oatbl, oe))) { ++ rno = ino_get(&oe->ref); ++ ++ if (oe->is_attr_name == 1) { ++ ntfs_free(oe->ptr); ++ oe->ptr = NULL; ++ continue; ++ } ++ ++ if (oe->is_attr_name) ++ continue; ++ ++ oa = oe->ptr; ++ if (!oa) ++ continue; ++ ++ run_close(&oa->run0); ++ ntfs_free(oa->attr); ++ if (oa->ni) ++ iput(&oa->ni->vfs_inode); ++ ntfs_free(oa); ++ } ++ ++ ntfs_free(trtbl); ++ ntfs_free(oatbl); ++ ntfs_free(dptbl); ++ ntfs_free(attr_names); ++ ntfs_free(rst_info.r_page); ++ ++ ntfs_free(ra); ++ ntfs_free(log->one_page_buf); ++ ++ if (err) ++ sbi->flags |= NTFS_FLAGS_NEED_REPLAY; ++ ++ if (err == -EROFS) ++ err = 0; ++ else if (log->set_dirty) ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ ++ ntfs_free(log); ++ ++ return err; ++} +-- +2.31.1 + diff --git a/SOURCES/0008-fs-ntfs3-Add-Kconfig-Makefile-and-doc.patch b/SOURCES/0008-fs-ntfs3-Add-Kconfig-Makefile-and-doc.patch new file mode 100644 index 0000000..ec60262 --- /dev/null +++ b/SOURCES/0008-fs-ntfs3-Add-Kconfig-Makefile-and-doc.patch @@ -0,0 +1,59 @@ +From 12dad495eaab95e0bb784c43869073617c513ea4 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 13 Aug 2021 17:21:30 +0300 +Subject: [Backport 12dad495eaab] src: Add Kconfig, Makefile and doc + +This adds Kconfig, Makefile and doc + +Signed-off-by: Konstantin Komarov +--- + src/Makefile | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 src/Makefile + +diff --git a/src/Makefile b/src/Makefile +new file mode 100644 +index 0000000000000000000000000000000000000000..279701b62bbeaed684abc1e2a4d448f71c403e95 +--- /dev/null ++++ b/src/Makefile +@@ -0,0 +1,36 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Makefile for the ntfs3 filesystem support. ++# ++ ++# to check robot warnings ++ccflags-y += -Wint-to-pointer-cast \ ++ $(call cc-option,-Wunused-but-set-variable,-Wunused-const-variable) \ ++ $(call cc-option,-Wold-style-declaration,-Wout-of-line-declaration) ++ ++obj-$(CONFIG_NTFS3_FS) += ntfs3.o ++ ++ntfs3-y := attrib.o \ ++ attrlist.o \ ++ bitfunc.o \ ++ bitmap.o \ ++ dir.o \ ++ fsntfs.o \ ++ frecord.o \ ++ file.o \ ++ fslog.o \ ++ inode.o \ ++ index.o \ ++ lznt.o \ ++ namei.o \ ++ record.o \ ++ run.o \ ++ super.o \ ++ upcase.o \ ++ xattr.o ++ ++ntfs3-$(CONFIG_NTFS3_LZX_XPRESS) += $(addprefix lib/,\ ++ decompress_common.o \ ++ lzx_decompress.o \ ++ xpress_decompress.o \ ++ ) +\ No newline at end of file +-- +2.31.1 + diff --git a/SOURCES/0009-fs-ntfs3-Fix-various-spelling-mistakes.patch b/SOURCES/0009-fs-ntfs3-Fix-various-spelling-mistakes.patch new file mode 100644 index 0000000..f6b8461 --- /dev/null +++ b/SOURCES/0009-fs-ntfs3-Fix-various-spelling-mistakes.patch @@ -0,0 +1,96 @@ +From f8d87ed9f0d546ac5b05e8e7d2b148d4b77599fa Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Mon, 16 Aug 2021 11:13:08 +0100 +Subject: [Backport f8d87ed9f0d5] src: Fix various spelling mistakes + +There is a spelling mistake in a ntfs_err error message. Also +fix various spelling mistakes in comments. + +Signed-off-by: Colin Ian King +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/debug.h | 2 +- + src/lib/decompress_common.c | 2 +- + src/run.c | 2 +- + src/super.c | 4 ++-- + src/upcase.c | 2 +- + 5 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/debug.h b/src/debug.h +index dfaa4c79dc6d86677b0b3de5c98a54a23ef637d6..15ac42185e5b39525ec7ead599975946d9d0b679 100644 +--- a/src/debug.h ++++ b/src/debug.h +@@ -3,7 +3,7 @@ + * + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * +- * useful functions for debuging ++ * useful functions for debugging + */ + + // clang-format off +diff --git a/src/lib/decompress_common.c b/src/lib/decompress_common.c +index 83c9e93aea77e437b5b1889b49272eee64d4df64..850d8e8c8f1fc75aa46e8f236b0c682daaf30d8f 100644 +--- a/src/lib/decompress_common.c ++++ b/src/lib/decompress_common.c +@@ -292,7 +292,7 @@ int make_huffman_decode_table(u16 decode_table[], const u32 num_syms, + * of as simply the root of the tree. The + * representation of these internal nodes is + * simply the index of the left child combined +- * with the special bits 0xC000 to distingush ++ * with the special bits 0xC000 to distinguish + * the entry from direct mapping and leaf node + * entries. + */ +diff --git a/src/run.c b/src/run.c +index 5cdf6efe67e0dbee860ce8462eae8de363ea5671..f9c362ac672e96b9430ab211de4952d6dce7797a 100644 +--- a/src/run.c ++++ b/src/run.c +@@ -949,7 +949,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + if (next_vcn > 0x100000000ull || (lcn + len) > 0x100000000ull) { + ntfs_err( + sbi->sb, +- "This driver is compiled whitout CONFIG_NTFS3_64BIT_CLUSTER (like windows driver).\n" ++ "This driver is compiled without CONFIG_NTFS3_64BIT_CLUSTER (like windows driver).\n" + "Volume contains 64 bits run: vcn %llx, lcn %llx, len %llx.\n" + "Activate CONFIG_NTFS3_64BIT_CLUSTER to process this case", + vcn64, lcn, len); +diff --git a/src/super.c b/src/super.c +index 6be13e256c1ad18d21ed5727cf8a1129840e774e..84d4f389f6855091f3dc9be77f50c5e6acf99706 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -124,7 +124,7 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + /* + * Shared memory struct. + * +- * on-disk ntfs's upcase table is created by ntfs formater ++ * on-disk ntfs's upcase table is created by ntfs formatter + * 'upcase' table is 128K bytes of memory + * we should read it into memory when mounting + * Several ntfs volumes likely use the same 'upcase' table +@@ -1208,7 +1208,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + sbi->def_entries = 1; + done = sizeof(struct ATTR_DEF_ENTRY); + sbi->reparse.max_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; +- sbi->ea_max_size = 0x10000; /* default formater value */ ++ sbi->ea_max_size = 0x10000; /* default formatter value */ + + while (done + sizeof(struct ATTR_DEF_ENTRY) <= bytes) { + u32 t32 = le32_to_cpu(t->type); +diff --git a/src/upcase.c b/src/upcase.c +index 9617382aca64b3ee322f7f769fbff38b8db52dbb..b53943538f9f2525764371ca0d925da295dbb10a 100644 +--- a/src/upcase.c ++++ b/src/upcase.c +@@ -27,7 +27,7 @@ static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr) + /* + * Thanks Kari Argillander for idea and implementation 'bothcase' + * +- * Straigth way to compare names: ++ * Straight way to compare names: + * - case insensitive + * - if name equals and 'bothcases' then + * - case sensitive +-- +2.31.1 + diff --git a/SOURCES/0010-fs-ntfs3-Use-linux-log2-is_power_of_2-function.patch b/SOURCES/0010-fs-ntfs3-Use-linux-log2-is_power_of_2-function.patch new file mode 100644 index 0000000..0952434 --- /dev/null +++ b/SOURCES/0010-fs-ntfs3-Use-linux-log2-is_power_of_2-function.patch @@ -0,0 +1,102 @@ +From 528c9b3d1edf291685151afecd741d176f527ddf Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Mon, 16 Aug 2021 13:37:32 +0300 +Subject: [Backport 528c9b3d1edf] src: Use linux/log2 is_power_of_2 + function + +We do not need our own implementation for this function in this +driver. It is much better to use generic one. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/ntfs_fs.h | 5 ----- + src/run.c | 3 ++- + src/super.c | 9 +++++---- + 3 files changed, 7 insertions(+), 10 deletions(-) + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 0c3ac89c3115da8131a66281c1c3c7da112d07ff..c8ea6dd38c213bf9ef5dd52aa46ae3cb3e8cd43b 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -972,11 +972,6 @@ static inline struct buffer_head *ntfs_bread(struct super_block *sb, + return NULL; + } + +-static inline bool is_power_of2(size_t v) +-{ +- return v && !(v & (v - 1)); +-} +- + static inline struct ntfs_inode *ntfs_i(struct inode *inode) + { + return container_of(inode, struct ntfs_inode, vfs_inode); +diff --git a/src/run.c b/src/run.c +index f9c362ac672e96b9430ab211de4952d6dce7797a..60c64deab7389261b9cb21f03118f2e8e2b8dc05 100644 +--- a/src/run.c ++++ b/src/run.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + + #include "debug.h" +@@ -376,7 +377,7 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + if (!used) { + bytes = 64; + } else if (used <= 16 * PAGE_SIZE) { +- if (is_power_of2(run->allocated)) ++ if (is_power_of_2(run->allocated)) + bytes = run->allocated << 1; + else + bytes = (size_t)1 +diff --git a/src/super.c b/src/super.c +index 84d4f389f6855091f3dc9be77f50c5e6acf99706..903975b7e832f627476fd8da4ec6e5fc9f080818 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -735,13 +736,13 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + + boot_sector_size = (u32)boot->bytes_per_sector[1] << 8; + if (boot->bytes_per_sector[0] || boot_sector_size < SECTOR_SIZE || +- !is_power_of2(boot_sector_size)) { ++ !is_power_of_2(boot_sector_size)) { + goto out; + } + + /* cluster size: 512, 1K, 2K, 4K, ... 2M */ + sct_per_clst = true_sectors_per_clst(boot); +- if (!is_power_of2(sct_per_clst)) ++ if (!is_power_of_2(sct_per_clst)) + goto out; + + mlcn = le64_to_cpu(boot->mft_clst); +@@ -757,14 +758,14 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + /* Check MFT record size */ + if ((boot->record_size < 0 && + SECTOR_SIZE > (2U << (-boot->record_size))) || +- (boot->record_size >= 0 && !is_power_of2(boot->record_size))) { ++ (boot->record_size >= 0 && !is_power_of_2(boot->record_size))) { + goto out; + } + + /* Check index record size */ + if ((boot->index_size < 0 && + SECTOR_SIZE > (2U << (-boot->index_size))) || +- (boot->index_size >= 0 && !is_power_of2(boot->index_size))) { ++ (boot->index_size >= 0 && !is_power_of_2(boot->index_size))) { + goto out; + } + +-- +2.31.1 + diff --git a/SOURCES/0011-fs-ntfs3-Add-ifndef-define-to-all-header-files.patch b/SOURCES/0011-fs-ntfs3-Add-ifndef-define-to-all-header-files.patch new file mode 100644 index 0000000..bed90f4 --- /dev/null +++ b/SOURCES/0011-fs-ntfs3-Add-ifndef-define-to-all-header-files.patch @@ -0,0 +1,79 @@ +From 87790b65343932411af43bc9b218f086ecebd6a5 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Mon, 16 Aug 2021 15:01:56 +0300 +Subject: [Backport 87790b653439] src: Add ifndef + define to all header + files + +Add guards so that compiler will only include header files once. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/debug.h | 5 +++++ + src/ntfs.h | 3 +++ + src/ntfs_fs.h | 5 +++++ + 3 files changed, 13 insertions(+) + +diff --git a/src/debug.h b/src/debug.h +index 15ac42185e5b39525ec7ead599975946d9d0b679..357d9f4dfba78792b443e05318d189c9167db1f8 100644 +--- a/src/debug.h ++++ b/src/debug.h +@@ -7,6 +7,9 @@ + */ + + // clang-format off ++#ifndef _LINUX_NTFS3_DEBUG_H ++#define _LINUX_NTFS3_DEBUG_H ++ + #ifndef Add2Ptr + #define Add2Ptr(P, I) ((void *)((u8 *)(P) + (I))) + #define PtrOffset(B, O) ((size_t)((size_t)(O) - (size_t)(B))) +@@ -61,4 +64,6 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + #define ntfs_free(p) kfree(p) + #define ntfs_vfree(p) kvfree(p) + #define ntfs_memdup(src, len) kmemdup(src, len, GFP_NOFS) ++ ++#endif /* _LINUX_NTFS3_DEBUG_H */ + // clang-format on +diff --git a/src/ntfs.h b/src/ntfs.h +index 40398e6c39c9383de1f917f6ef29d4d0238df7d7..16da514af124695bf751459672adb009301a9cdc 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -7,6 +7,8 @@ + */ + + // clang-format off ++#ifndef _LINUX_NTFS3_NTFS_H ++#define _LINUX_NTFS3_NTFS_H + + /* TODO: + * - Check 4K mft record and 512 bytes cluster +@@ -1235,4 +1237,5 @@ struct SID { + }; + static_assert(offsetof(struct SID, SubAuthority) == 8); + ++#endif /* _LINUX_NTFS3_NTFS_H */ + // clang-format on +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index c8ea6dd38c213bf9ef5dd52aa46ae3cb3e8cd43b..b5da2f06f7cbd087a3de12e0767fa2e5755033f9 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -6,6 +6,9 @@ + */ + + // clang-format off ++#ifndef _LINUX_NTFS3_NTFS_FS_H ++#define _LINUX_NTFS3_NTFS_FS_H ++ + #define MINUS_ONE_T ((size_t)(-1)) + /* Biggest MFT / smallest cluster */ + #define MAXIMUM_BYTES_PER_MFT 4096 +@@ -1085,3 +1088,5 @@ static inline void le64_sub_cpu(__le64 *var, u64 val) + { + *var = cpu_to_le64(le64_to_cpu(*var) - val); + } ++ ++#endif /* _LINUX_NTFS3_NTFS_FS_H */ +-- +2.31.1 + diff --git a/SOURCES/0012-fs-ntfs3-Fix-integer-overflow-in-multiplication.patch b/SOURCES/0012-fs-ntfs3-Fix-integer-overflow-in-multiplication.patch new file mode 100644 index 0000000..6460d38 --- /dev/null +++ b/SOURCES/0012-fs-ntfs3-Fix-integer-overflow-in-multiplication.patch @@ -0,0 +1,36 @@ +From 71eeb6ace80be7389d942b9647765417e5b039f7 Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Mon, 16 Aug 2021 17:30:25 +0100 +Subject: [Backport 71eeb6ace80b] src: Fix integer overflow in + multiplication + +The multiplication of the u32 data_size with a int is being performed +using 32 bit arithmetic however the results is being assigned to the +variable nbits that is a size_t (64 bit) value. Fix a potential +integer overflow by casting the u32 value to a size_t before the +multiply to use a size_t sized bit multiply operation. + +Addresses-Coverity: ("Unintentional integer overflow") +Fixes: 82cae269cfa9 ("src: Add initialization of super block") +Signed-off-by: Colin Ian King +Signed-off-by: Konstantin Komarov +--- + src/index.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/index.c b/src/index.c +index 6aa9540ece4728b5a02108924db76b4ee920a353..9386c551e208204870363685b27826ccb60e1171 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -2012,7 +2012,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni, + unsigned long pos; + const unsigned long *bm = resident_data(b); + +- nbits = le32_to_cpu(b->res.data_size) * 8; ++ nbits = (size_t)le32_to_cpu(b->res.data_size) * 8; + + if (bit >= nbits) + return 0; +-- +2.31.1 + diff --git a/SOURCES/0013-fs-ntfs3-Remove-unused-variable-cnt-in-ntfs_security.patch b/SOURCES/0013-fs-ntfs3-Remove-unused-variable-cnt-in-ntfs_security.patch new file mode 100644 index 0000000..a876223 --- /dev/null +++ b/SOURCES/0013-fs-ntfs3-Remove-unused-variable-cnt-in-ntfs_security.patch @@ -0,0 +1,58 @@ +From 8c01308b6d6b2bc8e9163c6a3400856fb782dee6 Mon Sep 17 00:00:00 2001 +From: Nathan Chancellor +Date: Mon, 16 Aug 2021 12:30:41 -0700 +Subject: [Backport 8c01308b6d6b] src: Remove unused variable cnt in + ntfs_security_init() + +Clang warns: + +src/fsntfs.c:1874:9: warning: variable 'cnt' set but not used +[-Wunused-but-set-variable] + size_t cnt, off; + ^ +1 warning generated. + +It is indeed unused so remove it. + +Fixes: 82cae269cfa9 ("src: Add initialization of super block") +Signed-off-by: Nathan Chancellor +Reviewed-by: Nick Desaulniers +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/fsntfs.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 92140050fb6c250ce263832d86ff15257cc035e4..c6599c514acf630183cd5eeef7a1630ea0ec6a75 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -1871,7 +1871,7 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + struct ATTRIB *attr; + struct ATTR_LIST_ENTRY *le; + u64 sds_size; +- size_t cnt, off; ++ size_t off; + struct NTFS_DE *ne; + struct NTFS_DE_SII *sii_e; + struct ntfs_fnd *fnd_sii = NULL; +@@ -1946,7 +1946,6 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + sbi->security.next_off = + Quad2Align(sds_size - SecurityDescriptorsBlockSize); + +- cnt = 0; + off = 0; + ne = NULL; + +@@ -1964,8 +1963,6 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + next_id = le32_to_cpu(sii_e->sec_id) + 1; + if (next_id >= sbi->security.next_id) + sbi->security.next_id = next_id; +- +- cnt += 1; + } + + sbi->security.ni = ni; +-- +2.31.1 + diff --git a/SOURCES/0014-fs-ntfs3-Fix-one-none-utf8-char-in-source-file.patch b/SOURCES/0014-fs-ntfs3-Fix-one-none-utf8-char-in-source-file.patch new file mode 100644 index 0000000..6cd4e42 --- /dev/null +++ b/SOURCES/0014-fs-ntfs3-Fix-one-none-utf8-char-in-source-file.patch @@ -0,0 +1,31 @@ +From be87e821fdb5ec8c6d404f29e118130c7879ce5b Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Wed, 18 Aug 2021 04:06:47 +0300 +Subject: [Backport be87e821fdb5] src: Fix one none utf8 char in source + file + +In one source file there is for some reason non utf8 char. But hey this +is fs development so this kind of thing might happen. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/frecord.c b/src/frecord.c +index c3121bf9c62fd82c27302b9337b1a5c6a4a61bef..e377d72477dfba95eb73a2a78a1c43ebc1f6f456 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -1784,7 +1784,7 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + /* + * WOF - Windows Overlay Filter - used to compress files with lzx/xpress + * Unlike native NTFS file compression, the Windows Overlay Filter supports +- * only read operations. This means that it doesn�t need to sector-align each ++ * only read operations. This means that it doesn't need to sector-align each + * compressed chunk, so the compressed data can be packed more tightly together. + * If you open the file for writing, the Windows Overlay Filter just decompresses + * the entire file, turning it back into a plain file. +-- +2.31.1 + diff --git a/SOURCES/0015-fs-ntfs3-Fix-fall-through-warnings-for-Clang.patch b/SOURCES/0015-fs-ntfs3-Fix-fall-through-warnings-for-Clang.patch new file mode 100644 index 0000000..7855423 --- /dev/null +++ b/SOURCES/0015-fs-ntfs3-Fix-fall-through-warnings-for-Clang.patch @@ -0,0 +1,49 @@ +From abfeb2ee2103f07dd93b9d7b32317e26d1c8ef79 Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Wed, 18 Aug 2021 17:21:46 -0500 +Subject: [Backport abfeb2ee2103] src: Fix fall-through warnings for Clang + +Fix the following fallthrough warnings: + +src/inode.c:1792:2: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough] +src/index.c:178:2: warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough] + +This helps with the ongoing efforts to globally enable +-Wimplicit-fallthrough for Clang. + +Link: https://github.com/KSPP/linux/issues/115 +Signed-off-by: Gustavo A. R. Silva +Reviewed-by: Nathan Chancellor +Signed-off-by: Konstantin Komarov +--- + src/index.c | 1 + + src/inode.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/src/index.c b/src/index.c +index 9386c551e208204870363685b27826ccb60e1171..189d46e2c38db2696073df47d778e1e190cc0d1f 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -175,6 +175,7 @@ static inline NTFS_CMP_FUNC get_cmp_func(const struct INDEX_ROOT *root) + default: + break; + } ++ break; + default: + break; + } +diff --git a/src/inode.c b/src/inode.c +index bf51e294432efebf20f75821fb25d2048eb7a15b..a573c6e98cb800ffd13871caac92b7eef2954c26 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1789,6 +1789,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) + switch (err) { + case 0: + drop_nlink(inode); ++ break; + case -ENOTEMPTY: + case -ENOSPC: + case -EROFS: +-- +2.31.1 + diff --git a/SOURCES/0016-fs-ntfs3-Remove-unused-including-linux-version.h.patch b/SOURCES/0016-fs-ntfs3-Remove-unused-including-linux-version.h.patch new file mode 100644 index 0000000..bc3e933 --- /dev/null +++ b/SOURCES/0016-fs-ntfs3-Remove-unused-including-linux-version.h.patch @@ -0,0 +1,34 @@ +From 1263eddfea9988125a4b9608efecc8aff2c721f9 Mon Sep 17 00:00:00 2001 +From: Jiapeng Chong +Date: Thu, 19 Aug 2021 16:23:37 +0800 +Subject: [Backport 1263eddfea99] src: Remove unused including + + +Eliminate the follow versioncheck warning: + +./src/inode.c: 16 linux/version.h not needed. + +Reported-by: Abaci Robot +Fixes: 82cae269cfa9 ("src: Add initialization of super block") +Signed-off-by: Jiapeng Chong +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/inode.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/inode.c b/src/inode.c +index a573c6e98cb800ffd13871caac92b7eef2954c26..ed64489edf73ddec8fd582851d3ef2ec79625d27 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -13,7 +13,6 @@ + #include + #include + #include +-#include + #include + + #include "debug.h" +-- +2.31.1 + diff --git a/SOURCES/0017-fs-ntfs3-Restyle-comment-block-in-ni_parse_reparse.patch b/SOURCES/0017-fs-ntfs3-Restyle-comment-block-in-ni_parse_reparse.patch new file mode 100644 index 0000000..fc3fcf4 --- /dev/null +++ b/SOURCES/0017-fs-ntfs3-Restyle-comment-block-in-ni_parse_reparse.patch @@ -0,0 +1,51 @@ +From 24516d481dfc6c7728ffe36280ca8cf4640d119a Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 24 Aug 2021 21:37:06 +0300 +Subject: [Backport 24516d481dfc] src: Restyle comment block in + ni_parse_reparse() + +First of this fix one none utf8 char in this comment block. Maybe +this happened because error in filesystem ;) + +Also this block was hard to read because long lines so make it max 80 +long. And while we doing this stuff make little better grammer. + +Signed-off-by: Kari Argillander +Reviewed-by: Christoph Hellwig +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/src/frecord.c b/src/frecord.c +index e377d72477dfba95eb73a2a78a1c43ebc1f6f456..e94d830b2f4a12f84bf4870219507b21042b41c0 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -1782,14 +1782,18 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + break; + case IO_REPARSE_TAG_COMPRESS: + /* +- * WOF - Windows Overlay Filter - used to compress files with lzx/xpress +- * Unlike native NTFS file compression, the Windows Overlay Filter supports +- * only read operations. This means that it doesn't need to sector-align each +- * compressed chunk, so the compressed data can be packed more tightly together. +- * If you open the file for writing, the Windows Overlay Filter just decompresses ++ * WOF - Windows Overlay Filter - Used to compress files with ++ * LZX/Xpress. ++ * ++ * Unlike native NTFS file compression, the Windows ++ * Overlay Filter supports only read operations. This means ++ * that it doesn't need to sector-align each compressed chunk, ++ * so the compressed data can be packed more tightly together. ++ * If you open the file for writing, the WOF just decompresses + * the entire file, turning it back into a plain file. + * +- * ntfs3 driver decompresses the entire file only on write or change size requests ++ * Ntfs3 driver decompresses the entire file only on write or ++ * change size requests. + */ + + cmpr = &rp->CompressReparseBuffer; +-- +2.31.1 + diff --git a/SOURCES/0018-fs-ntfs3-Use-kernel-ALIGN-macros-over-driver-specifi.patch b/SOURCES/0018-fs-ntfs3-Use-kernel-ALIGN-macros-over-driver-specifi.patch new file mode 100644 index 0000000..3b79973 --- /dev/null +++ b/SOURCES/0018-fs-ntfs3-Use-kernel-ALIGN-macros-over-driver-specifi.patch @@ -0,0 +1,575 @@ +From fa3cacf544636b2dc48cfb2f277a2071f14d66a2 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 26 Aug 2021 11:56:29 +0300 +Subject: [Backport fa3cacf54463] src: Use kernel ALIGN macros over driver + specific + +The static checkers (Smatch) were complaining because QuadAlign() was +buggy. If you try to align something higher than UINT_MAX it got +truncated to a u32. + +Smatch warning was: + src/attrib.c:383 attr_set_size_res() + warn: was expecting a 64 bit value instead of '~7' + +So that this will not happen again we will change all these macros to +kernel made ones. This can also help some other static analyzing tools +to give us better warnings. + +Patch was generated with Coccinelle script and after that some style +issue was hand fixed. + +Coccinelle script: + +virtual patch + +@alloc depends on patch@ +expression x; +@@ +( +- #define QuadAlign(n) (((n) + 7u) & (~7u)) +| +- QuadAlign(x) ++ ALIGN(x, 8) +| +- #define IsQuadAligned(n) (!((size_t)(n)&7u)) +| +- IsQuadAligned(x) ++ IS_ALIGNED(x, 8) +| +- #define Quad2Align(n) (((n) + 15u) & (~15u)) +| +- Quad2Align(x) ++ ALIGN(x, 16) +| +- #define IsQuad2Aligned(n) (!((size_t)(n)&15u)) +| +- IsQuad2Aligned(x) ++ IS_ALIGNED(x, 16) +| +- #define Quad4Align(n) (((n) + 31u) & (~31u)) +| +- Quad4Align(x) ++ ALIGN(x, 32) +| +- #define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1))) +| +- IsSizeTAligned(x) ++ IS_ALIGNED(x, sizeof(size_t)) +| +- #define DwordAlign(n) (((n) + 3u) & (~3u)) +| +- DwordAlign(x) ++ ALIGN(x, 4) +| +- #define IsDwordAligned(n) (!((size_t)(n)&3u)) +| +- IsDwordAligned(x) ++ IS_ALIGNED(x, 4) +| +- #define WordAlign(n) (((n) + 1u) & (~1u)) +| +- WordAlign(x) ++ ALIGN(x, 2) +| +- #define IsWordAligned(n) (!((size_t)(n)&1u)) +| +- IsWordAligned(x) ++ IS_ALIGNED(x, 2) +| +) + +Reported-by: Dan Carpenter +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 2 +- + src/debug.h | 11 ----------- + src/frecord.c | 14 +++++++------- + src/fslog.c | 32 ++++++++++++++++---------------- + src/fsntfs.c | 4 ++-- + src/index.c | 4 ++-- + src/inode.c | 8 ++++---- + src/namei.c | 2 +- + src/ntfs.h | 16 ++++++++-------- + src/ntfs_fs.h | 2 +- + src/record.c | 10 +++++----- + src/super.c | 10 +++++----- + src/xattr.c | 8 ++++---- + 13 files changed, 56 insertions(+), 67 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index 046dc57f75f2169b47e7078c251f26b05ec69b42..c15467ec12ed88ad05390fb57a47f396d4391f3e 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -380,7 +380,7 @@ static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr, + u32 rsize = le32_to_cpu(attr->res.data_size); + u32 tail = used - aoff - asize; + char *next = Add2Ptr(attr, asize); +- s64 dsize = QuadAlign(new_size) - QuadAlign(rsize); ++ s64 dsize = ALIGN(new_size, 8) - ALIGN(rsize, 8); + + if (dsize < 0) { + memmove(next + dsize, next, tail); +diff --git a/src/debug.h b/src/debug.h +index 357d9f4dfba78792b443e05318d189c9167db1f8..aff4cead9f58ff3222d68d54d853808f0e17a6dc 100644 +--- a/src/debug.h ++++ b/src/debug.h +@@ -15,17 +15,6 @@ + #define PtrOffset(B, O) ((size_t)((size_t)(O) - (size_t)(B))) + #endif + +-#define QuadAlign(n) (((n) + 7u) & (~7u)) +-#define IsQuadAligned(n) (!((size_t)(n)&7u)) +-#define Quad2Align(n) (((n) + 15u) & (~15u)) +-#define IsQuad2Aligned(n) (!((size_t)(n)&15u)) +-#define Quad4Align(n) (((n) + 31u) & (~31u)) +-#define IsSizeTAligned(n) (!((size_t)(n) & (sizeof(size_t) - 1))) +-#define DwordAlign(n) (((n) + 3u) & (~3u)) +-#define IsDwordAligned(n) (!((size_t)(n)&3u)) +-#define WordAlign(n) (((n) + 1u) & (~1u)) +-#define IsWordAligned(n) (!((size_t)(n)&1u)) +- + #ifdef CONFIG_PRINTK + __printf(2, 3) + void ntfs_printk(const struct super_block *sb, const char *fmt, ...); +diff --git a/src/frecord.c b/src/frecord.c +index e94d830b2f4a12f84bf4870219507b21042b41c0..8e2242147ebfcdc4bea3cf19d79276725f09be9b 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -1249,7 +1249,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + if (err < 0) + goto out; + +- run_size = QuadAlign(err); ++ run_size = ALIGN(err, 8); + err = 0; + + if (plen < svcn) { +@@ -1269,7 +1269,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + if (err < 0) + goto out; + +- run_size = QuadAlign(err); ++ run_size = ALIGN(err, 8); + err = 0; + + if (plen < evcn + 1 - svcn) { +@@ -1392,7 +1392,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + struct ATTRIB *attr; + bool is_ext = + (flags & (ATTR_FLAG_SPARSED | ATTR_FLAG_COMPRESSED)) && !svcn; +- u32 name_size = QuadAlign(name_len * sizeof(short)); ++ u32 name_size = ALIGN(name_len * sizeof(short), 8); + u32 name_off = is_ext ? SIZEOF_NONRESIDENT_EX : SIZEOF_NONRESIDENT; + u32 run_off = name_off + name_size; + u32 run_size, asize; +@@ -1403,7 +1403,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (err < 0) + goto out; + +- run_size = QuadAlign(err); ++ run_size = ALIGN(err, 8); + + if (plen < len) { + err = -EINVAL; +@@ -1463,8 +1463,8 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + struct ATTRIB **new_attr, struct mft_inode **mi) + { + int err; +- u32 name_size = QuadAlign(name_len * sizeof(short)); +- u32 asize = SIZEOF_RESIDENT + name_size + QuadAlign(data_size); ++ u32 name_size = ALIGN(name_len * sizeof(short), 8); ++ u32 asize = SIZEOF_RESIDENT + name_size + ALIGN(data_size, 8); + struct ATTRIB *attr; + + err = ni_insert_attr(ni, type, name, name_len, asize, SIZEOF_RESIDENT, +@@ -2853,7 +2853,7 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + } else if (!attr->non_res) { + u32 data_size = le32_to_cpu(attr->res.data_size); + +- dup->alloc_size = cpu_to_le64(QuadAlign(data_size)); ++ dup->alloc_size = cpu_to_le64(ALIGN(data_size, 8)); + dup->data_size = cpu_to_le64(data_size); + } else { + u64 new_valid = ni->i_valid; +diff --git a/src/fslog.c b/src/fslog.c +index 397ba6a956e7f0e25661c88c0c91ec089c4d2103..5a118c351441cc2deca390c49cfd87d75f3e587b 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -456,7 +456,7 @@ static inline bool is_rst_page_hdr_valid(u32 file_off, + return false; + + ro = le16_to_cpu(rhdr->ra_off); +- if (!IsQuadAligned(ro) || ro > sys_page) ++ if (!IS_ALIGNED(ro, 8) || ro > sys_page) + return false; + + end_usa = ((sys_page >> SECTOR_SHIFT) + 1) * sizeof(short); +@@ -488,7 +488,7 @@ static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) + + off = le16_to_cpu(ra->client_off); + +- if (!IsQuadAligned(off) || ro + off > SECTOR_SIZE - sizeof(short)) ++ if (!IS_ALIGNED(off, 8) || ro + off > SECTOR_SIZE - sizeof(short)) + return false; + + off += cl * sizeof(struct CLIENT_REC); +@@ -526,8 +526,8 @@ static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) + } + + /* The log page data offset and record header length must be quad-aligned */ +- if (!IsQuadAligned(le16_to_cpu(ra->data_off)) || +- !IsQuadAligned(le16_to_cpu(ra->rec_hdr_len))) ++ if (!IS_ALIGNED(le16_to_cpu(ra->data_off), 8) || ++ !IS_ALIGNED(le16_to_cpu(ra->rec_hdr_len), 8)) + return false; + + return true; +@@ -1355,9 +1355,9 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + log->l_flags |= NTFSLOG_MULTIPLE_PAGE_IO; + + /* Compute the log page values */ +- log->data_off = QuadAlign( ++ log->data_off = ALIGN( + offsetof(struct RECORD_PAGE_HDR, fixups) + +- sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1)); ++ sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1), 8); + log->data_size = log->page_size - log->data_off; + log->record_header_len = sizeof(struct LFS_RECORD_HDR); + +@@ -1365,9 +1365,9 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + log->reserved = log->data_size - log->record_header_len; + + /* Compute the restart page values. */ +- log->ra_off = QuadAlign( ++ log->ra_off = ALIGN( + offsetof(struct RESTART_HDR, fixups) + +- sizeof(short) * ((log->sys_page_size >> SECTOR_SHIFT) + 1)); ++ sizeof(short) * ((log->sys_page_size >> SECTOR_SHIFT) + 1), 8); + log->restart_size = log->sys_page_size - log->ra_off; + log->ra_size = struct_size(log->ra, clients, 1); + log->current_openlog_count = open_log_count; +@@ -1496,7 +1496,7 @@ static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, + + vbo = hdr_off + log->data_off; + } else { +- vbo = QuadAlign(end); ++ vbo = ALIGN(end, 8); + } + + /* Compute the lsn based on the file offset and the sequence count */ +@@ -2982,7 +2982,7 @@ static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi, + __le16 flags) + { + struct ATTRIB *attr; +- u32 name_size = QuadAlign(name_len * sizeof(short)); ++ u32 name_size = ALIGN(name_len * sizeof(short), 8); + bool is_ext = flags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED); + u32 asize = name_size + + (is_ext ? SIZEOF_NONRESIDENT_EX : SIZEOF_NONRESIDENT); +@@ -3220,7 +3220,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + goto dirty_vol; + + memmove(attr, attr2, dlen); +- rec->used = cpu_to_le32(QuadAlign(roff + dlen)); ++ rec->used = cpu_to_le32(ALIGN(roff + dlen, 8)); + + mi->dirty = true; + break; +@@ -3231,7 +3231,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + used = le32_to_cpu(rec->used); + + if (!check_if_attr(rec, lrh) || dlen < SIZEOF_RESIDENT || +- !IsQuadAligned(asize) || ++ !IS_ALIGNED(asize, 8) || + Add2Ptr(attr2, asize) > Add2Ptr(lrh, rec_len) || + dlen > record_size - used) { + goto dirty_vol; +@@ -3296,7 +3296,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + if (nsize > asize && nsize - asize > record_size - used) + goto dirty_vol; + +- nsize = QuadAlign(nsize); ++ nsize = ALIGN(nsize, 8); + data_off = le16_to_cpu(attr->res.data_off); + + if (nsize < asize) { +@@ -3341,7 +3341,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + goto dirty_vol; + } + +- nsize = QuadAlign(nsize); ++ nsize = ALIGN(nsize, 8); + + memmove(Add2Ptr(attr, nsize), Add2Ptr(attr, asize), + used - le16_to_cpu(lrh->record_off) - asize); +@@ -5103,8 +5103,8 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + rh->sys_page_size = cpu_to_le32(log->page_size); + rh->page_size = cpu_to_le32(log->page_size); + +- t16 = QuadAlign(offsetof(struct RESTART_HDR, fixups) + +- sizeof(short) * t16); ++ t16 = ALIGN(offsetof(struct RESTART_HDR, fixups) + ++ sizeof(short) * t16, 8); + rh->ra_off = cpu_to_le16(t16); + rh->minor_ver = cpu_to_le16(1); // 0x1A: + rh->major_ver = cpu_to_le16(1); // 0x1C: +diff --git a/src/fsntfs.c b/src/fsntfs.c +index c6599c514acf630183cd5eeef7a1630ea0ec6a75..e887921a117d9124c6d4987397a694f7c99a09ea 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -1944,7 +1944,7 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + sbi->security.next_id = SECURITY_ID_FIRST; + /* Always write new security at the end of bucket */ + sbi->security.next_off = +- Quad2Align(sds_size - SecurityDescriptorsBlockSize); ++ ALIGN(sds_size - SecurityDescriptorsBlockSize, 16); + + off = 0; + ne = NULL; +@@ -2096,7 +2096,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + struct NTFS_DE_SII sii_e; + struct SECURITY_HDR *d_security; + u32 new_sec_size = size_sd + SIZEOF_SECURITY_HDR; +- u32 aligned_sec_size = Quad2Align(new_sec_size); ++ u32 aligned_sec_size = ALIGN(new_sec_size, 16); + struct SECURITY_KEY hash_key; + struct ntfs_fnd *fnd_sdh = NULL; + const struct INDEX_ROOT *root_sdh; +diff --git a/src/index.c b/src/index.c +index 189d46e2c38db2696073df47d778e1e190cc0d1f..cc03cb953dd0c6f76aa900a7ee8ae2daf41d55b2 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -702,7 +702,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + + if (max_idx >= nslots) { + u16 *ptr; +- int new_slots = QuadAlign(2 * nslots); ++ int new_slots = ALIGN(2 * nslots, 8); + + ptr = ntfs_malloc(sizeof(u16) * new_slots); + if (ptr) +@@ -959,7 +959,7 @@ static struct indx_node *indx_new(struct ntfs_index *indx, + index->rhdr.fix_num = cpu_to_le16(fn); + index->vbn = cpu_to_le64(vbn); + hdr = &index->ihdr; +- eo = QuadAlign(sizeof(struct INDEX_BUFFER) + fn * sizeof(short)); ++ eo = ALIGN(sizeof(struct INDEX_BUFFER) + fn * sizeof(short), 8); + hdr->de_off = cpu_to_le32(eo); + + e = Add2Ptr(hdr, eo); +diff --git a/src/inode.c b/src/inode.c +index ed64489edf73ddec8fd582851d3ef2ec79625d27..3a857e575ef23d059b5eb761d3b91df52a592037 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1335,7 +1335,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + fname->dup.ea_size = fname->dup.reparse = 0; + + dsize = le16_to_cpu(new_de->key_size); +- asize = QuadAlign(SIZEOF_RESIDENT + dsize); ++ asize = ALIGN(SIZEOF_RESIDENT + dsize, 8); + + attr->type = ATTR_NAME; + attr->size = cpu_to_le32(asize); +@@ -1349,7 +1349,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + if (security_id == SECURITY_ID_INVALID) { + /* Insert security attribute */ +- asize = SIZEOF_RESIDENT + QuadAlign(sd_size); ++ asize = SIZEOF_RESIDENT + ALIGN(sd_size, 8); + + attr->type = ATTR_SECURE; + attr->size = cpu_to_le32(asize); +@@ -1472,7 +1472,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + attr->id = cpu_to_le16(aid++); + + /* resident or non resident? */ +- asize = QuadAlign(SIZEOF_RESIDENT + nsize); ++ asize = ALIGN(SIZEOF_RESIDENT + nsize, 8); + t16 = PtrOffset(rec, attr); + + if (asize + t16 + 8 > sbi->record_size) { +@@ -1508,7 +1508,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + goto out5; + } + +- asize = SIZEOF_NONRESIDENT + QuadAlign(err); ++ asize = SIZEOF_NONRESIDENT + ALIGN(err, 8); + inode->i_size = nsize; + } else { + attr->res.data_off = SIZEOF_RESIDENT_LE; +diff --git a/src/namei.c b/src/namei.c +index b1ccd66172f29d2e3a324ab620483c44602294f7..0626844e6bdc72ccd21d839175205875d4b52f63 100644 +--- a/src/namei.c ++++ b/src/namei.c +@@ -57,7 +57,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, + fname->type = FILE_NAME_POSIX; + data_size = fname_full_size(fname); + +- e->size = cpu_to_le16(QuadAlign(data_size) + sizeof(struct NTFS_DE)); ++ e->size = cpu_to_le16(ALIGN(data_size, 8) + sizeof(struct NTFS_DE)); + e->key_size = cpu_to_le16(data_size); + e->flags = 0; + e->res = 0; +diff --git a/src/ntfs.h b/src/ntfs.h +index 16da514af124695bf751459672adb009301a9cdc..f6d6f1bd312e75ac5c5a61c9baa3d4cb7c5c9f19 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -392,8 +392,8 @@ static inline u64 attr_ondisk_size(const struct ATTRIB *attr) + return attr->non_res ? ((attr->flags & + (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) ? + le64_to_cpu(attr->nres.total_size) : +- le64_to_cpu(attr->nres.alloc_size)) : +- QuadAlign(le32_to_cpu(attr->res.data_size)); ++ le64_to_cpu(attr->nres.alloc_size)) ++ : ALIGN(le32_to_cpu(attr->res.data_size), 8); + } + + static inline u64 attr_size(const struct ATTRIB *attr) +@@ -529,8 +529,8 @@ static_assert(sizeof(struct ATTR_LIST_ENTRY) == 0x20); + + static inline u32 le_size(u8 name_len) + { +- return QuadAlign(offsetof(struct ATTR_LIST_ENTRY, name) + +- name_len * sizeof(short)); ++ return ALIGN(offsetof(struct ATTR_LIST_ENTRY, name) + ++ name_len * sizeof(short), 8); + } + + /* returns 0 if 'attr' has the same type and name */ +@@ -691,10 +691,10 @@ static inline bool de_has_vcn_ex(const struct NTFS_DE *e) + sizeof(__le64))); + } + +-#define MAX_BYTES_PER_NAME_ENTRY \ +- QuadAlign(sizeof(struct NTFS_DE) + \ +- offsetof(struct ATTR_FILE_NAME, name) + \ +- NTFS_NAME_LEN * sizeof(short)) ++#define MAX_BYTES_PER_NAME_ENTRY \ ++ ALIGN(sizeof(struct NTFS_DE) + \ ++ offsetof(struct ATTR_FILE_NAME, name) + \ ++ NTFS_NAME_LEN * sizeof(short), 8) + + struct INDEX_HDR { + __le32 de_off; // 0x00: The offset from the start of this structure +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index b5da2f06f7cbd087a3de12e0767fa2e5755033f9..d4dd19b822bc2678c3e3432a2d73f21ce0f31256 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -900,7 +900,7 @@ static inline bool run_is_empty(struct runs_tree *run) + /* NTFS uses quad aligned bitmaps */ + static inline size_t bitmap_size(size_t bits) + { +- return QuadAlign((bits + 7) >> 3); ++ return ALIGN((bits + 7) >> 3, 8); + } + + #define _100ns2seconds 10000000 +diff --git a/src/record.c b/src/record.c +index 0d4a6251bddcbd689db9eab4993ccc9675ab3838..721c14f83e2b37389a2aa23b7bb1aeffab5fd5fb 100644 +--- a/src/record.c ++++ b/src/record.c +@@ -206,7 +206,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + return NULL; + + if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 || +- !IsDwordAligned(off)) { ++ !IS_ALIGNED(off, 4)) { + return NULL; + } + +@@ -235,7 +235,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + + /* Can we use the first field (attr->type) */ + if (off + 8 > used) { +- static_assert(QuadAlign(sizeof(enum ATTR_TYPE)) == 8); ++ static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8); + return NULL; + } + +@@ -539,7 +539,7 @@ bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes) + next = Add2Ptr(attr, asize); + + if (bytes > 0) { +- dsize = QuadAlign(bytes); ++ dsize = ALIGN(bytes, 8); + if (used + dsize > total) + return false; + nsize = asize + dsize; +@@ -549,7 +549,7 @@ bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes) + used += dsize; + rsize += dsize; + } else { +- dsize = QuadAlign(-bytes); ++ dsize = ALIGN(-bytes, 8); + if (dsize > asize) + return false; + nsize = asize - dsize; +@@ -596,7 +596,7 @@ int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr, + return err; + } + +- new_run_size = QuadAlign(err); ++ new_run_size = ALIGN(err, 8); + + memmove(next + new_run_size - run_size, next + dsize, tail); + +diff --git a/src/super.c b/src/super.c +index 903975b7e832f627476fd8da4ec6e5fc9f080818..7a501bca26d7916d23dc64d7919d4ad1384d5765 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -809,9 +809,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + sbi->attr_size_tr = (5 * record_size >> 4); // ~320 bytes + + sbi->max_bytes_per_attr = +- record_size - QuadAlign(MFTRECORD_FIXUP_OFFSET_1) - +- QuadAlign(((record_size >> SECTOR_SHIFT) * sizeof(short))) - +- QuadAlign(sizeof(enum ATTR_TYPE)); ++ record_size - ALIGN(MFTRECORD_FIXUP_OFFSET_1, 8) - ++ ALIGN(((record_size >> SECTOR_SHIFT) * sizeof(short)), 8) - ++ ALIGN(sizeof(enum ATTR_TYPE), 8); + + sbi->index_size = boot->index_size < 0 + ? 1u << (-boot->index_size) +@@ -859,9 +859,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + rec->rhdr.fix_off = cpu_to_le16(MFTRECORD_FIXUP_OFFSET_1); + fn = (sbi->record_size >> SECTOR_SHIFT) + 1; + rec->rhdr.fix_num = cpu_to_le16(fn); +- ao = QuadAlign(MFTRECORD_FIXUP_OFFSET_1 + sizeof(short) * fn); ++ ao = ALIGN(MFTRECORD_FIXUP_OFFSET_1 + sizeof(short) * fn, 8); + rec->attr_off = cpu_to_le16(ao); +- rec->used = cpu_to_le32(ao + QuadAlign(sizeof(enum ATTR_TYPE))); ++ rec->used = cpu_to_le32(ao + ALIGN(sizeof(enum ATTR_TYPE), 8)); + rec->total = cpu_to_le32(sbi->record_size); + ((struct ATTRIB *)Add2Ptr(rec, ao))->type = ATTR_END; + +diff --git a/src/xattr.c b/src/xattr.c +index 98871c895e77b165bede3fbd1e50b91d5e07b106..6b17d46b9506ccaa6a6a06dfaf4dde00524186e2 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -26,9 +26,9 @@ + static inline size_t unpacked_ea_size(const struct EA_FULL *ea) + { + return ea->size ? le32_to_cpu(ea->size) +- : DwordAlign(struct_size( +- ea, name, +- 1 + ea->name_len + le16_to_cpu(ea->elength))); ++ : ALIGN(struct_size( ++ ea, name, ++ 1 + ea->name_len + le16_to_cpu(ea->elength)), 4); + } + + static inline size_t packed_ea_size(const struct EA_FULL *ea) +@@ -289,7 +289,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + goto out; + } + +- add = DwordAlign(struct_size(ea_all, name, 1 + name_len + val_size)); ++ add = ALIGN(struct_size(ea_all, name, 1 + name_len + val_size), 4); + + err = ntfs_read_ea(ni, &ea_all, add, &info); + if (err) +-- +2.31.1 + diff --git a/SOURCES/0019-fs-ntfs3-Do-not-use-driver-own-alloc-wrappers.patch b/SOURCES/0019-fs-ntfs3-Do-not-use-driver-own-alloc-wrappers.patch new file mode 100644 index 0000000..728e232 --- /dev/null +++ b/SOURCES/0019-fs-ntfs3-Do-not-use-driver-own-alloc-wrappers.patch @@ -0,0 +1,1583 @@ +From 195c52bdd5d5ecfdabf5a7c6159efe299e534f84 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 24 Aug 2021 21:37:07 +0300 +Subject: [Backport 195c52bdd5d5] src: Do not use driver own alloc + wrappers + +Problem with these wrapper is that we cannot take off example GFP_NOFS +flag. It is not recomended use those in all places. Also if we change +one driver specific wrapper to kernel wrapper then it would look really +weird. People should be most familiar with kernel wrappers so let's just +use those ones. + +Driver specific alloc wrapper also confuse some static analyzing tools, +good example is example kernels checkpatch tool. After we converter +these to kernel specific then warnings is showed. + +Following Coccinelle script was used to automate changing. + +virtual patch + +@alloc depends on patch@ +expression x; +expression y; +@@ +( +- ntfs_malloc(x) ++ kmalloc(x, GFP_NOFS) +| +- ntfs_zalloc(x) ++ kzalloc(x, GFP_NOFS) +| +- ntfs_vmalloc(x) ++ kvmalloc(x, GFP_NOFS) +| +- ntfs_free(x) ++ kfree(x) +| +- ntfs_vfree(x) ++ kvfree(x) +| +- ntfs_memdup(x, y) ++ kmemdup(x, y, GFP_NOFS) +) + +Reviewed-by: Christoph Hellwig +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 6 +- + src/attrlist.c | 10 +-- + src/bitmap.c | 8 +-- + src/debug.h | 7 -- + src/file.c | 4 +- + src/frecord.c | 27 +++---- + src/fslog.c | 172 ++++++++++++++++++++++---------------------- + src/fsntfs.c | 8 +-- + src/index.c | 54 +++++++------- + src/inode.c | 10 +-- + src/lznt.c | 4 +- + src/ntfs_fs.h | 18 ++--- + src/record.c | 8 +-- + src/run.c | 8 +-- + src/super.c | 20 +++--- + src/xattr.c | 18 ++--- + 16 files changed, 188 insertions(+), 194 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index c15467ec12ed88ad05390fb57a47f396d4391f3e..4eae9886e27d0f481671c4b40c22a3742d794e4f 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -276,7 +276,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + run_init(run); + + /* make a copy of original attribute */ +- attr_s = ntfs_memdup(attr, asize); ++ attr_s = kmemdup(attr, asize, GFP_NOFS); + if (!attr_s) { + err = -ENOMEM; + goto out; +@@ -333,7 +333,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + if (err) + goto out3; + +- ntfs_free(attr_s); ++ kfree(attr_s); + attr->nres.data_size = cpu_to_le64(rsize); + attr->nres.valid_size = attr->nres.data_size; + +@@ -356,7 +356,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + run_deallocate(sbi, run, false); + run_close(run); + out1: +- ntfs_free(attr_s); ++ kfree(attr_s); + /*reinsert le*/ + out: + return err; +diff --git a/src/attrlist.c b/src/attrlist.c +index ea561361b576d5a8a1ec30c0017a5ac76f1a61df..348bfb54db09f364f64182f51468f6cceb12e37d 100644 +--- a/src/attrlist.c ++++ b/src/attrlist.c +@@ -28,7 +28,7 @@ static inline bool al_is_valid_le(const struct ntfs_inode *ni, + void al_destroy(struct ntfs_inode *ni) + { + run_close(&ni->attr_list.run); +- ntfs_free(ni->attr_list.le); ++ kfree(ni->attr_list.le); + ni->attr_list.le = NULL; + ni->attr_list.size = 0; + ni->attr_list.dirty = false; +@@ -51,7 +51,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) + + if (!attr->non_res) { + lsize = le32_to_cpu(attr->res.data_size); +- le = ntfs_malloc(al_aligned(lsize)); ++ le = kmalloc(al_aligned(lsize), GFP_NOFS); + if (!le) { + err = -ENOMEM; + goto out; +@@ -74,7 +74,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) + if (err < 0) + goto out; + +- le = ntfs_malloc(al_aligned(lsize)); ++ le = kmalloc(al_aligned(lsize), GFP_NOFS); + if (!le) { + err = -ENOMEM; + goto out; +@@ -289,7 +289,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + off = PtrOffset(al->le, le); + + if (new_size > asize) { +- void *ptr = ntfs_malloc(new_asize); ++ void *ptr = kmalloc(new_asize, GFP_NOFS); + + if (!ptr) + return -ENOMEM; +@@ -297,7 +297,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + memcpy(ptr, al->le, off); + memcpy(Add2Ptr(ptr, off + sz), le, al->size - off); + le = Add2Ptr(ptr, off); +- ntfs_free(al->le); ++ kfree(al->le); + al->le = ptr; + } else { + memmove(Add2Ptr(le, sz), le, al->size - off); +diff --git a/src/bitmap.c b/src/bitmap.c +index 32aab0031221db3ee67b09c239b960158370d238..d502bba323d00e1ccafa7a0a0eeea46e0e36d796 100644 +--- a/src/bitmap.c ++++ b/src/bitmap.c +@@ -133,7 +133,7 @@ void wnd_close(struct wnd_bitmap *wnd) + { + struct rb_node *node, *next; + +- ntfs_free(wnd->free_bits); ++ kfree(wnd->free_bits); + run_close(&wnd->run); + + node = rb_first(&wnd->start_tree); +@@ -683,7 +683,7 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits) + if (!wnd->bits_last) + wnd->bits_last = wbits; + +- wnd->free_bits = ntfs_zalloc(wnd->nwnd * sizeof(u16)); ++ wnd->free_bits = kzalloc(wnd->nwnd * sizeof(u16), GFP_NOFS); + if (!wnd->free_bits) + return -ENOMEM; + +@@ -1354,7 +1354,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + new_last = wbits; + + if (new_wnd != wnd->nwnd) { +- new_free = ntfs_malloc(new_wnd * sizeof(u16)); ++ new_free = kmalloc(new_wnd * sizeof(u16), GFP_NOFS); + if (!new_free) + return -ENOMEM; + +@@ -1363,7 +1363,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + wnd->nwnd * sizeof(short)); + memset(new_free + wnd->nwnd, 0, + (new_wnd - wnd->nwnd) * sizeof(short)); +- ntfs_free(wnd->free_bits); ++ kfree(wnd->free_bits); + wnd->free_bits = new_free; + } + +diff --git a/src/debug.h b/src/debug.h +index aff4cead9f58ff3222d68d54d853808f0e17a6dc..c8403e876b2dc522520921c58fa230a4df4e04d1 100644 +--- a/src/debug.h ++++ b/src/debug.h +@@ -47,12 +47,5 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + #define ntfs_inode_warn(inode, fmt, ...) \ + ntfs_inode_printk(inode, KERN_WARNING fmt, ##__VA_ARGS__) + +-#define ntfs_malloc(s) kmalloc(s, GFP_NOFS) +-#define ntfs_zalloc(s) kzalloc(s, GFP_NOFS) +-#define ntfs_vmalloc(s) kvmalloc(s, GFP_KERNEL) +-#define ntfs_free(p) kfree(p) +-#define ntfs_vfree(p) kvfree(p) +-#define ntfs_memdup(src, len) kmemdup(src, len, GFP_NOFS) +- + #endif /* _LINUX_NTFS3_DEBUG_H */ + // clang-format on +diff --git a/src/file.c b/src/file.c +index 59344985c2e8017d67cfba642d1bacaa676b3f24..8d27c520bec56a1daca965fba7ba826049b18709 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -900,7 +900,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + return -EOPNOTSUPP; + } + +- pages = ntfs_malloc(pages_per_frame * sizeof(struct page *)); ++ pages = kmalloc(pages_per_frame * sizeof(struct page *), GFP_NOFS); + if (!pages) + return -ENOMEM; + +@@ -1076,7 +1076,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + } + + out: +- ntfs_free(pages); ++ kfree(pages); + + current->backing_dev_info = NULL; + +diff --git a/src/frecord.c b/src/frecord.c +index 8e2242147ebfcdc4bea3cf19d79276725f09be9b..2f7d165435307dc37dfbc6ba7959f04c1b218257 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -388,7 +388,7 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + { + struct mft_inode *m; + +- m = ntfs_zalloc(sizeof(struct mft_inode)); ++ m = kzalloc(sizeof(struct mft_inode), GFP_NOFS); + if (!m) + return false; + +@@ -752,7 +752,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + run_deallocate(sbi, &ni->attr_list.run, true); + run_close(&ni->attr_list.run); + ni->attr_list.size = 0; +- ntfs_free(ni->attr_list.le); ++ kfree(ni->attr_list.le); + ni->attr_list.le = NULL; + ni->attr_list.dirty = false; + +@@ -787,7 +787,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) + * Skip estimating exact memory requirement + * Looks like one record_size is always enough + */ +- le = ntfs_malloc(al_aligned(rs)); ++ le = kmalloc(al_aligned(rs), GFP_NOFS); + if (!le) { + err = -ENOMEM; + goto out; +@@ -893,7 +893,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) + goto out; + + out1: +- ntfs_free(ni->attr_list.le); ++ kfree(ni->attr_list.le); + ni->attr_list.le = NULL; + ni->attr_list.size = 0; + +@@ -2054,7 +2054,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + idx = (vbo - frame_vbo) >> PAGE_SHIFT; + + pages_per_frame = frame_size >> PAGE_SHIFT; +- pages = ntfs_zalloc(pages_per_frame * sizeof(struct page *)); ++ pages = kzalloc(pages_per_frame * sizeof(struct page *), GFP_NOFS); + if (!pages) { + err = -ENOMEM; + goto out; +@@ -2092,7 +2092,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + + out: + /* At this point, err contains 0 or -EIO depending on the "critical" page */ +- ntfs_free(pages); ++ kfree(pages); + unlock_page(page); + + return err; +@@ -2137,7 +2137,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + frame_bits = ni_ext_compress_bits(ni); + frame_size = 1u << frame_bits; + pages_per_frame = frame_size >> PAGE_SHIFT; +- pages = ntfs_zalloc(pages_per_frame * sizeof(struct page *)); ++ pages = kzalloc(pages_per_frame * sizeof(struct page *), GFP_NOFS); + if (!pages) { + err = -ENOMEM; + goto out; +@@ -2298,7 +2298,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + mapping->a_ops = &ntfs_aops; + + out: +- ntfs_free(pages); ++ kfree(pages); + if (err) { + make_bad_inode(inode); + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); +@@ -2564,7 +2564,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + goto out1; + } + +- pages_disk = ntfs_zalloc(npages_disk * sizeof(struct page *)); ++ pages_disk = kzalloc(npages_disk * sizeof(struct page *), GFP_NOFS); + if (!pages_disk) { + err = -ENOMEM; + goto out2; +@@ -2633,7 +2633,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + put_page(pg); + } + } +- ntfs_free(pages_disk); ++ kfree(pages_disk); + + out2: + #ifdef CONFIG_NTFS3_LZX_XPRESS +@@ -2709,7 +2709,8 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + goto out; + } + +- pages_disk = ntfs_zalloc(pages_per_frame * sizeof(struct page *)); ++ pages_disk = kzalloc(pages_per_frame * sizeof(struct page *), ++ GFP_NOFS); + if (!pages_disk) { + err = -ENOMEM; + goto out; +@@ -2769,7 +2770,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + compr_size = compress_lznt(frame_mem, frame_size, frame_ondisk, + frame_size, sbi->compress.lznt); + mutex_unlock(&sbi->compress.mtx_lznt); +- ntfs_free(lznt); ++ kfree(lznt); + + if (compr_size + sbi->cluster_size > frame_size) { + /* frame is not compressed */ +@@ -2818,7 +2819,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + put_page(pg); + } + } +- ntfs_free(pages_disk); ++ kfree(pages_disk); + out: + return err; + } +diff --git a/src/fslog.c b/src/fslog.c +index 5a118c351441cc2deca390c49cfd87d75f3e587b..2c213b55979ed06c03f4e74998fd03f9a9a8827b 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -406,9 +406,9 @@ struct lcb { + static void lcb_put(struct lcb *lcb) + { + if (lcb->alloc) +- ntfs_free(lcb->log_rec); +- ntfs_free(lcb->lrh); +- ntfs_free(lcb); ++ kfree(lcb->log_rec); ++ kfree(lcb->lrh); ++ kfree(lcb); + } + + /* +@@ -807,7 +807,7 @@ static inline struct RESTART_TABLE *init_rsttbl(u16 esize, u16 used) + u32 off; + u32 bytes = esize * used + sizeof(struct RESTART_TABLE); + u32 lf = sizeof(struct RESTART_TABLE) + (used - 1) * esize; +- struct RESTART_TABLE *t = ntfs_zalloc(bytes); ++ struct RESTART_TABLE *t = kzalloc(bytes, GFP_NOFS); + + t->size = cpu_to_le16(esize); + t->used = cpu_to_le16(used); +@@ -849,7 +849,7 @@ static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl, + + rt->total = tbl->total; + +- ntfs_free(tbl); ++ kfree(tbl); + return rt; + } + +@@ -1134,7 +1134,7 @@ static int read_log_page(struct ntfs_log *log, u32 vbo, + return -EINVAL; + + if (!*buffer) { +- to_free = ntfs_malloc(bytes); ++ to_free = kmalloc(bytes, GFP_NOFS); + if (!to_free) + return -ENOMEM; + *buffer = to_free; +@@ -1164,7 +1164,7 @@ static int read_log_page(struct ntfs_log *log, u32 vbo, + + out: + if (err && to_free) { +- ntfs_free(to_free); ++ kfree(to_free); + *buffer = NULL; + } + +@@ -1181,7 +1181,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + struct restart_info *info) + { + u32 skip, vbo; +- struct RESTART_HDR *r_page = ntfs_malloc(DefaultLogPageSize); ++ struct RESTART_HDR *r_page = kmalloc(DefaultLogPageSize, GFP_NOFS); + + if (!r_page) + return -ENOMEM; +@@ -1257,8 +1257,8 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + /* Read the entire restart area */ + sys_page_size = le32_to_cpu(r_page->sys_page_size); + if (DefaultLogPageSize != sys_page_size) { +- ntfs_free(r_page); +- r_page = ntfs_zalloc(sys_page_size); ++ kfree(r_page); ++ r_page = kzalloc(sys_page_size, GFP_NOFS); + if (!r_page) + return -ENOMEM; + +@@ -1266,7 +1266,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + (struct RECORD_PAGE_HDR **)&r_page, + &usa_error)) { + /* ignore any errors */ +- ntfs_free(r_page); ++ kfree(r_page); + r_page = NULL; + continue; + } +@@ -1296,7 +1296,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + } + } + +- ntfs_free(r_page); ++ kfree(r_page); + + return 0; + } +@@ -1397,7 +1397,7 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + static struct RESTART_AREA *log_create_ra(struct ntfs_log *log) + { + struct CLIENT_REC *cr; +- struct RESTART_AREA *ra = ntfs_zalloc(log->restart_size); ++ struct RESTART_AREA *ra = kzalloc(log->restart_size, GFP_NOFS); + + if (!ra) + return NULL; +@@ -1509,7 +1509,7 @@ static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, + if (!is_lsn_in_file(log, *lsn)) + *lsn = 0; + +- ntfs_free(page); ++ kfree(page); + + return 0; + } +@@ -1634,7 +1634,7 @@ static int last_log_lsn(struct ntfs_log *log) + second_off = 0x12 * log->page_size; + + // 0x10 == 0x12 - 0x2 +- page_bufs = ntfs_malloc(log->page_size * 0x10); ++ page_bufs = kmalloc(log->page_size * 0x10, GFP_NOFS); + if (!page_bufs) + return -ENOMEM; + } else { +@@ -1646,7 +1646,7 @@ static int last_log_lsn(struct ntfs_log *log) + /* Read second tail page (at pos 3/0x12000) */ + if (read_log_page(log, second_off, &second_tail, &usa_error) || + usa_error || second_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) { +- ntfs_free(second_tail); ++ kfree(second_tail); + second_tail = NULL; + second_file_off = 0; + lsn2 = 0; +@@ -1658,7 +1658,7 @@ static int last_log_lsn(struct ntfs_log *log) + /* Read first tail page (at pos 2/0x2000 ) */ + if (read_log_page(log, final_off, &first_tail, &usa_error) || + usa_error || first_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) { +- ntfs_free(first_tail); ++ kfree(first_tail); + first_tail = NULL; + first_file_off = 0; + lsn1 = 0; +@@ -1759,17 +1759,17 @@ static int last_log_lsn(struct ntfs_log *log) + page_pos = page_cnt = 1; + } + } else { +- ntfs_free(first_tail); +- ntfs_free(second_tail); ++ kfree(first_tail); ++ kfree(second_tail); + goto tail_read; + } + +- ntfs_free(first_tail_prev); ++ kfree(first_tail_prev); + first_tail_prev = first_tail; + final_off_prev = first_file_off; + first_tail = NULL; + +- ntfs_free(second_tail_prev); ++ kfree(second_tail_prev); + second_tail_prev = second_tail; + second_off_prev = second_file_off; + second_tail = NULL; +@@ -2030,7 +2030,7 @@ static int last_log_lsn(struct ntfs_log *log) + } + + curpage_off = nextpage_off; +- ntfs_free(page); ++ kfree(page); + page = NULL; + reuse_page = 0; + goto next_page; +@@ -2092,7 +2092,7 @@ static int last_log_lsn(struct ntfs_log *log) + cur_pos = 2; + + next_test_page: +- ntfs_free(tst_page); ++ kfree(tst_page); + tst_page = NULL; + + /* Walk through the file, reading log pages */ +@@ -2151,7 +2151,7 @@ static int last_log_lsn(struct ntfs_log *log) + } + + /* Call our routine to check this log page */ +- ntfs_free(tst_page); ++ kfree(tst_page); + tst_page = NULL; + + err = read_log_page(log, nextpage_off, &tst_page, &usa_error); +@@ -2186,7 +2186,7 @@ static int last_log_lsn(struct ntfs_log *log) + u64 off = hdr_file_off(log, tmp_page); + + if (!page) { +- page = ntfs_malloc(log->page_size); ++ page = kmalloc(log->page_size, GFP_NOFS); + if (!page) + return -ENOMEM; + } +@@ -2231,11 +2231,11 @@ static int last_log_lsn(struct ntfs_log *log) + } + + out: +- ntfs_free(second_tail); +- ntfs_free(first_tail); +- ntfs_free(page); +- ntfs_free(tst_page); +- ntfs_free(page_bufs); ++ kfree(second_tail); ++ kfree(first_tail); ++ kfree(page); ++ kfree(tst_page); ++ kfree(page_bufs); + + return err; + } +@@ -2311,7 +2311,7 @@ static int read_log_rec_buf(struct ntfs_log *log, + } + + out: +- ntfs_free(ph); ++ kfree(ph); + return err; + } + +@@ -2360,7 +2360,7 @@ static int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_, + goto out; + } + +- rst = ntfs_malloc(len); ++ rst = kmalloc(len, GFP_NOFS); + if (!rst) { + err = -ENOMEM; + goto out; +@@ -2375,8 +2375,8 @@ static int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_, + rst = NULL; + + out: +- ntfs_free(rh); +- ntfs_free(rst); ++ kfree(rh); ++ kfree(rst); + + return err; + } +@@ -2419,7 +2419,7 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + * put a pointer to the log record the context block + */ + if (rh->flags & LOG_RECORD_MULTI_PAGE) { +- void *lr = ntfs_malloc(len); ++ void *lr = kmalloc(len, GFP_NOFS); + + if (!lr) + return -ENOMEM; +@@ -2472,7 +2472,7 @@ static int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode, + if (!verify_client_lsn(log, cr, lsn)) + return -EINVAL; + +- lcb = ntfs_zalloc(sizeof(struct lcb)); ++ lcb = kzalloc(sizeof(struct lcb), GFP_NOFS); + if (!lcb) + return -ENOMEM; + lcb->client = log->client_id; +@@ -2521,7 +2521,7 @@ static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + break; + + if (hdr != lcb->lrh) +- ntfs_free(hdr); ++ kfree(hdr); + + hdr = NULL; + err = read_log_page(log, lsn_to_vbo(log, current_lsn), +@@ -2533,7 +2533,7 @@ static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + sizeof(struct CLIENT_ID))) { + /*err = -EINVAL; */ + } else if (LfsClientRecord == hdr->record_type) { +- ntfs_free(lcb->lrh); ++ kfree(lcb->lrh); + lcb->lrh = hdr; + *lsn = current_lsn; + return 0; +@@ -2542,7 +2542,7 @@ static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + + out: + if (hdr != lcb->lrh) +- ntfs_free(hdr); ++ kfree(hdr); + return err; + + check_undo_next: +@@ -2566,7 +2566,7 @@ static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + (struct RECORD_PAGE_HDR **)&hdr, NULL); + if (err) + return err; +- ntfs_free(lcb->lrh); ++ kfree(lcb->lrh); + lcb->lrh = hdr; + + *lsn = next_lsn; +@@ -2586,11 +2586,11 @@ static int read_next_log_rec(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + return 0; + + if (lcb->alloc) +- ntfs_free(lcb->log_rec); ++ kfree(lcb->log_rec); + + lcb->log_rec = NULL; + lcb->alloc = false; +- ntfs_free(lcb->lrh); ++ kfree(lcb->lrh); + lcb->lrh = NULL; + + return find_log_rec(log, *lsn, lcb); +@@ -2987,7 +2987,7 @@ static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi, + u32 asize = name_size + + (is_ext ? SIZEOF_NONRESIDENT_EX : SIZEOF_NONRESIDENT); + +- attr = ntfs_zalloc(asize); ++ attr = kzalloc(asize, GFP_NOFS); + if (!attr) + return NULL; + +@@ -3087,7 +3087,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + if (inode) { + mi = &ntfs_i(inode)->mi; + } else if (op == InitializeFileRecordSegment) { +- mi = ntfs_zalloc(sizeof(struct mft_inode)); ++ mi = kzalloc(sizeof(struct mft_inode), GFP_NOFS); + if (!mi) + return -ENOMEM; + err = mi_format_new(mi, sbi, rno, 0, false); +@@ -3181,7 +3181,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + if (attr->type == ATTR_ALLOC) + bytes = (bytes + 511) & ~511; // align + +- buffer_le = ntfs_malloc(bytes); ++ buffer_le = kmalloc(bytes, GFP_NOFS); + if (!buffer_le) + return -ENOMEM; + +@@ -3250,11 +3250,11 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + + oa2 = find_loaded_attr(log, attr, rno_base); + if (oa2) { +- void *p2 = ntfs_memdup(attr, le32_to_cpu(attr->size)); +- ++ void *p2 = kmemdup(attr, le32_to_cpu(attr->size), ++ GFP_NOFS); + if (p2) { + // run_close(oa2->run1); +- ntfs_free(oa2->attr); ++ kfree(oa2->attr); + oa2->attr = p2; + } + } +@@ -3317,12 +3317,12 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + + oa2 = find_loaded_attr(log, attr, rno_base); + if (oa2) { +- void *p2 = ntfs_memdup(attr, le32_to_cpu(attr->size)); +- ++ void *p2 = kmemdup(attr, le32_to_cpu(attr->size), ++ GFP_NOFS); + if (p2) { + // run_close(&oa2->run0); + oa2->run1 = &oa2->run0; +- ntfs_free(oa2->attr); ++ kfree(oa2->attr); + oa2->attr = p2; + } + } +@@ -3376,10 +3376,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + + oa2 = find_loaded_attr(log, attr, rno_base); + if (oa2) { +- void *p2 = ntfs_memdup(attr, le32_to_cpu(attr->size)); +- ++ void *p2 = kmemdup(attr, le32_to_cpu(attr->size), ++ GFP_NOFS); + if (p2) { +- ntfs_free(oa2->attr); ++ kfree(oa2->attr); + oa2->attr = p2; + } + } +@@ -3714,7 +3714,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + else if (mi != mi2_child) + mi_put(mi); + +- ntfs_free(buffer_le); ++ kfree(buffer_le); + + return err; + +@@ -3783,13 +3783,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (!page_size) + return -EINVAL; + +- log = ntfs_zalloc(sizeof(struct ntfs_log)); ++ log = kzalloc(sizeof(struct ntfs_log), GFP_NOFS); + if (!log) + return -ENOMEM; + + log->ni = ni; + log->l_size = l_size; +- log->one_page_buf = ntfs_malloc(page_size); ++ log->one_page_buf = kmalloc(page_size, GFP_NOFS); + + if (!log->one_page_buf) { + err = -ENOMEM; +@@ -3854,17 +3854,17 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + sp->rhdr.sign == NTFS_CHKD_SIGNATURE) { + use_second_page = false; + } +- ntfs_free(sp); ++ kfree(sp); + } + + if (use_second_page) { +- ntfs_free(rst_info.r_page); ++ kfree(rst_info.r_page); + memcpy(&rst_info, &rst_info2, sizeof(struct restart_info)); + rst_info2.r_page = NULL; + } + + use_first_page: +- ntfs_free(rst_info2.r_page); ++ kfree(rst_info2.r_page); + + check_restart_area: + /* If the restart area is at offset 0, we want to write the second restart area first */ +@@ -4012,7 +4012,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + log->current_avail = current_log_avail(log); + +- ra = ntfs_zalloc(log->restart_size); ++ ra = kzalloc(log->restart_size, GFP_NOFS); + if (!ra) { + err = -ENOMEM; + goto out; +@@ -4147,7 +4147,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- trtbl = ntfs_memdup(rt, t32); ++ trtbl = kmemdup(rt, t32, GFP_NOFS); + if (!trtbl) { + err = -ENOMEM; + goto out; +@@ -4187,7 +4187,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- dptbl = ntfs_memdup(rt, t32); ++ dptbl = kmemdup(rt, t32, GFP_NOFS); + if (!dptbl) { + err = -ENOMEM; + goto out; +@@ -4254,7 +4254,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + t32 = lrh_length(lrh); + rec_len -= t32; + +- attr_names = ntfs_memdup(Add2Ptr(lrh, t32), rec_len); ++ attr_names = kmemdup(Add2Ptr(lrh, t32), rec_len, GFP_NOFS); + + lcb_put(lcb); + lcb = NULL; +@@ -4289,7 +4289,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- oatbl = ntfs_memdup(rt, t32); ++ oatbl = kmemdup(rt, t32, GFP_NOFS); + if (!oatbl) { + err = -ENOMEM; + goto out; +@@ -4472,7 +4472,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + sizeof(u64); + } else { + t32 = log->clst_per_page; +- ntfs_free(dptbl); ++ kfree(dptbl); + dptbl = init_rsttbl(struct_size(dp, page_lcns, t32), + 32); + if (!dptbl) { +@@ -4575,7 +4575,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + t16 = le16_to_cpu(lrh->undo_len); + if (t16) { +- oe->ptr = ntfs_malloc(t16); ++ oe->ptr = kmalloc(t16, GFP_NOFS); + if (!oe->ptr) { + err = -ENOMEM; + goto out; +@@ -4680,7 +4680,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto next_dirty_page; + } + +- oa = ntfs_zalloc(sizeof(struct OpenAttr)); ++ oa = kzalloc(sizeof(struct OpenAttr), GFP_NOFS); + if (!oa) { + err = -ENOMEM; + goto out; +@@ -4701,7 +4701,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + attr = attr_create_nonres_log(sbi, oe->type, 0, oe->ptr, + oe->name_len, 0); + if (!attr) { +- ntfs_free(oa); ++ kfree(oa); + err = -ENOMEM; + goto out; + } +@@ -4720,7 +4720,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto fake_attr; + + t32 = le32_to_cpu(attr->size); +- oa->attr = ntfs_memdup(attr, t32); ++ oa->attr = kmemdup(attr, t32, GFP_NOFS); + if (!oa->attr) + goto fake_attr; + +@@ -4746,7 +4746,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + le64_to_cpu(attr->nres.evcn), svcn, + Add2Ptr(attr, roff), t32 - roff); + if (err < 0) { +- ntfs_free(oa->attr); ++ kfree(oa->attr); + oa->attr = NULL; + goto fake_attr; + } +@@ -4757,7 +4757,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + final_oe: + if (oe->is_attr_name == 1) +- ntfs_free(oe->ptr); ++ kfree(oe->ptr); + oe->is_attr_name = 0; + oe->ptr = oa; + oe->name_len = attr->name_len; +@@ -5090,7 +5090,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (is_ro) + goto out; + +- rh = ntfs_zalloc(log->page_size); ++ rh = kzalloc(log->page_size, GFP_NOFS); + if (!rh) { + err = -ENOMEM; + goto out; +@@ -5125,12 +5125,12 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + err = ntfs_sb_write_run(sbi, &log->ni->file.run, log->page_size, + rh, log->page_size); + +- ntfs_free(rh); ++ kfree(rh); + if (err) + goto out; + + out: +- ntfs_free(rst); ++ kfree(rst); + if (lcb) + lcb_put(lcb); + +@@ -5140,7 +5140,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + rno = ino_get(&oe->ref); + + if (oe->is_attr_name == 1) { +- ntfs_free(oe->ptr); ++ kfree(oe->ptr); + oe->ptr = NULL; + continue; + } +@@ -5153,20 +5153,20 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + continue; + + run_close(&oa->run0); +- ntfs_free(oa->attr); ++ kfree(oa->attr); + if (oa->ni) + iput(&oa->ni->vfs_inode); +- ntfs_free(oa); ++ kfree(oa); + } + +- ntfs_free(trtbl); +- ntfs_free(oatbl); +- ntfs_free(dptbl); +- ntfs_free(attr_names); +- ntfs_free(rst_info.r_page); ++ kfree(trtbl); ++ kfree(oatbl); ++ kfree(dptbl); ++ kfree(attr_names); ++ kfree(rst_info.r_page); + +- ntfs_free(ra); +- ntfs_free(log->one_page_buf); ++ kfree(ra); ++ kfree(log->one_page_buf); + + if (err) + sbi->flags |= NTFS_FLAGS_NEED_REPLAY; +@@ -5176,7 +5176,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + else if (log->set_dirty) + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + +- ntfs_free(log); ++ kfree(log); + + return err; + } +diff --git a/src/fsntfs.c b/src/fsntfs.c +index e887921a117d9124c6d4987397a694f7c99a09ea..fb2652c8dd74c9934a73d0076409a95e3bb6815f 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -2035,7 +2035,7 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, + + *size = t32 - SIZEOF_SECURITY_HDR; + +- p = ntfs_malloc(*size); ++ p = kmalloc(*size, GFP_NOFS); + if (!p) { + err = -ENOMEM; + goto out; +@@ -2063,7 +2063,7 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, + p = NULL; + + out: +- ntfs_free(p); ++ kfree(p); + fnd_put(fnd_sii); + ni_unlock(ni); + +@@ -2115,7 +2115,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + *security_id = SECURITY_ID_INVALID; + + /* Allocate a temporal buffer*/ +- d_security = ntfs_zalloc(aligned_sec_size); ++ d_security = kzalloc(aligned_sec_size, GFP_NOFS); + if (!d_security) + return -ENOMEM; + +@@ -2279,7 +2279,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + fnd_put(fnd_sdh); + mark_inode_dirty(&ni->vfs_inode); + ni_unlock(ni); +- ntfs_free(d_security); ++ kfree(d_security); + + return err; + } +diff --git a/src/index.c b/src/index.c +index cc03cb953dd0c6f76aa900a7ee8ae2daf41d55b2..334a3cef714b3e5c5b6fc31b3fd33d39bb5caa25 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -682,7 +682,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + if (end > 0x10000) + goto next; + +- offs = ntfs_malloc(sizeof(u16) * nslots); ++ offs = kmalloc(sizeof(u16) * nslots, GFP_NOFS); + if (!offs) + goto next; + +@@ -704,10 +704,10 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + u16 *ptr; + int new_slots = ALIGN(2 * nslots, 8); + +- ptr = ntfs_malloc(sizeof(u16) * new_slots); ++ ptr = kmalloc(sizeof(u16) * new_slots, GFP_NOFS); + if (ptr) + memcpy(ptr, offs, sizeof(u16) * max_idx); +- ntfs_free(offs); ++ kfree(offs); + offs = ptr; + nslots = new_slots; + if (!ptr) +@@ -764,7 +764,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + e = Add2Ptr(hdr, offs[fnd]); + + out1: +- ntfs_free(offs); ++ kfree(offs); + + return e; + #endif +@@ -934,21 +934,21 @@ static struct indx_node *indx_new(struct ntfs_index *indx, + u16 fn; + u32 eo; + +- r = ntfs_zalloc(sizeof(struct indx_node)); ++ r = kzalloc(sizeof(struct indx_node), GFP_NOFS); + if (!r) + return ERR_PTR(-ENOMEM); + +- index = ntfs_zalloc(bytes); ++ index = kzalloc(bytes, GFP_NOFS); + if (!index) { +- ntfs_free(r); ++ kfree(r); + return ERR_PTR(-ENOMEM); + } + + err = ntfs_get_bh(ni->mi.sbi, &indx->alloc_run, vbo, bytes, &r->nb); + + if (err) { +- ntfs_free(index); +- ntfs_free(r); ++ kfree(index); ++ kfree(r); + return ERR_PTR(err); + } + +@@ -1027,7 +1027,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn, + const struct INDEX_NAMES *name; + + if (!in) { +- in = ntfs_zalloc(sizeof(struct indx_node)); ++ in = kzalloc(sizeof(struct indx_node), GFP_NOFS); + if (!in) + return -ENOMEM; + } else { +@@ -1036,7 +1036,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn, + + ib = in->index; + if (!ib) { +- ib = ntfs_malloc(bytes); ++ ib = kmalloc(bytes, GFP_NOFS); + if (!ib) { + err = -ENOMEM; + goto out; +@@ -1083,11 +1083,11 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn, + + out: + if (ib != in->index) +- ntfs_free(ib); ++ kfree(ib); + + if (*node != in) { + nb_put(&in->nb); +- ntfs_free(in); ++ kfree(in); + } + + return err; +@@ -1219,7 +1219,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + sizeof(struct NTFS_DE) + sizeof(u64)) { + if (n) { + fnd_pop(fnd); +- ntfs_free(n); ++ kfree(n); + } + return -EINVAL; + } +@@ -1232,7 +1232,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + /* Try next level */ + e = hdr_first_de(&n->index->ihdr); + if (!e) { +- ntfs_free(n); ++ kfree(n); + return -EINVAL; + } + +@@ -1252,7 +1252,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + /* Pop one level */ + if (n) { + fnd_pop(fnd); +- ntfs_free(n); ++ kfree(n); + } + + level = fnd->level; +@@ -1589,7 +1589,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* Make a copy of root attribute to restore if error */ +- a_root = ntfs_memdup(attr, asize); ++ a_root = kmemdup(attr, asize, GFP_NOFS); + if (!a_root) { + err = -ENOMEM; + goto out; +@@ -1615,7 +1615,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + if (!to_move) { + re = NULL; + } else { +- re = ntfs_memdup(e0, to_move); ++ re = kmemdup(e0, to_move, GFP_NOFS); + if (!re) { + err = -ENOMEM; + goto out; +@@ -1708,7 +1708,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + * new entry classic case when mft record is 1K and index + * buffer 4K the problem should not occurs + */ +- ntfs_free(re); ++ kfree(re); + indx_write(indx, ni, n, 0); + + put_indx_node(n); +@@ -1734,12 +1734,12 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + n = NULL; + + out1: +- ntfs_free(re); ++ kfree(re); + if (n) + put_indx_node(n); + + out: +- ntfs_free(a_root); ++ kfree(a_root); + return err; + } + +@@ -1792,7 +1792,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + return -EINVAL; + + sp_size = le16_to_cpu(sp->size); +- up_e = ntfs_malloc(sp_size + sizeof(u64)); ++ up_e = kmalloc(sp_size + sizeof(u64), GFP_NOFS); + if (!up_e) + return -ENOMEM; + memcpy(up_e, sp, sp_size); +@@ -1870,7 +1870,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + out: +- ntfs_free(up_e); ++ kfree(up_e); + + return err; + } +@@ -2149,7 +2149,7 @@ static int indx_get_entry_to_replace(struct ntfs_index *indx, + n = fnd->nodes[level]; + te = hdr_first_de(&n->index->ihdr); + /* Copy the candidate entry into the replacement entry buffer. */ +- re = ntfs_malloc(le16_to_cpu(te->size) + sizeof(u64)); ++ re = kmalloc(le16_to_cpu(te->size) + sizeof(u64), GFP_NOFS); + if (!re) { + err = -ENOMEM; + goto out; +@@ -2301,7 +2301,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + fnd) + : indx_insert_into_root(indx, ni, re, e, + ctx, fnd); +- ntfs_free(re); ++ kfree(re); + + if (err) + goto out; +@@ -2459,7 +2459,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + * as appropriate. + */ + e_size = le16_to_cpu(e->size); +- me = ntfs_memdup(e, e_size); ++ me = kmemdup(e, e_size, GFP_NOFS); + if (!me) { + err = -ENOMEM; + goto out; +@@ -2505,7 +2505,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + * Find the spot the tree where we want to insert the new entry. + */ + err = indx_insert_entry(indx, ni, me, ctx, fnd); +- ntfs_free(me); ++ kfree(me); + if (err) + goto out; + +diff --git a/src/inode.c b/src/inode.c +index 3a857e575ef23d059b5eb761d3b91df52a592037..520471f35e298cdafc929c1e262409ad91ccb250 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1096,7 +1096,7 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname, + __le16 *rp_name; + typeof(rp->SymbolicLinkReparseBuffer) *rs; + +- rp = ntfs_zalloc(ntfs_reparse_bytes(2 * size + 2)); ++ rp = kzalloc(ntfs_reparse_bytes(2 * size + 2), GFP_NOFS); + if (!rp) + return ERR_PTR(-ENOMEM); + +@@ -1151,7 +1151,7 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname, + + return rp; + out: +- ntfs_free(rp); ++ kfree(rp); + return ERR_PTR(err); + } + +@@ -1619,7 +1619,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + out2: + __putname(new_de); +- ntfs_free(rp); ++ kfree(rp); + + out1: + if (err) +@@ -1862,7 +1862,7 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + goto out; + } + } else { +- rp = ntfs_malloc(i_size); ++ rp = kmalloc(i_size, GFP_NOFS); + if (!rp) { + err = -ENOMEM; + goto out; +@@ -1972,7 +1972,7 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + /* Always set last zero */ + buffer[err] = 0; + out: +- ntfs_free(to_free); ++ kfree(to_free); + return err; + } + +diff --git a/src/lznt.c b/src/lznt.c +index ead9ab7d69b3082e429ac79b973aab461d3cfd7a..eb43b9f5149b0d0e6364c6226b426f61d7e651de 100644 +--- a/src/lznt.c ++++ b/src/lznt.c +@@ -294,8 +294,8 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, + */ + struct lznt *get_lznt_ctx(int level) + { +- struct lznt *r = ntfs_zalloc(level ? offsetof(struct lznt, hash) +- : sizeof(struct lznt)); ++ struct lznt *r = kzalloc(level ? offsetof(struct lznt, hash) : ++ sizeof(struct lznt), GFP_NOFS); + + if (r) + r->std = !level; +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index d4dd19b822bc2678c3e3432a2d73f21ce0f31256..e3a667e9838f3290032e90aaded68c5b09edbb70 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -603,13 +603,13 @@ int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit); + void fnd_clear(struct ntfs_fnd *fnd); + static inline struct ntfs_fnd *fnd_get(void) + { +- return ntfs_zalloc(sizeof(struct ntfs_fnd)); ++ return kzalloc(sizeof(struct ntfs_fnd), GFP_NOFS); + } + static inline void fnd_put(struct ntfs_fnd *fnd) + { + if (fnd) { + fnd_clear(fnd); +- ntfs_free(fnd); ++ kfree(fnd); + } + } + void indx_clear(struct ntfs_index *idx); +@@ -875,20 +875,20 @@ static inline void run_init(struct runs_tree *run) + + static inline struct runs_tree *run_alloc(void) + { +- return ntfs_zalloc(sizeof(struct runs_tree)); ++ return kzalloc(sizeof(struct runs_tree), GFP_NOFS); + } + + static inline void run_close(struct runs_tree *run) + { +- ntfs_vfree(run->runs); ++ kvfree(run->runs); + memset(run, 0, sizeof(*run)); + } + + static inline void run_free(struct runs_tree *run) + { + if (run) { +- ntfs_vfree(run->runs); +- ntfs_free(run); ++ kvfree(run->runs); ++ kfree(run); + } + } + +@@ -1044,15 +1044,15 @@ static inline void put_indx_node(struct indx_node *in) + if (!in) + return; + +- ntfs_free(in->index); ++ kfree(in->index); + nb_put(&in->nb); +- ntfs_free(in); ++ kfree(in); + } + + static inline void mi_clear(struct mft_inode *mi) + { + nb_put(&mi->nb); +- ntfs_free(mi->mrec); ++ kfree(mi->mrec); + mi->mrec = NULL; + } + +diff --git a/src/record.c b/src/record.c +index 721c14f83e2b37389a2aa23b7bb1aeffab5fd5fb..dec83ac815c7983c1367cd451fd5985aa3d425c3 100644 +--- a/src/record.c ++++ b/src/record.c +@@ -76,14 +76,14 @@ static __le16 mi_new_attt_id(struct mft_inode *mi) + int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi) + { + int err; +- struct mft_inode *m = ntfs_zalloc(sizeof(struct mft_inode)); ++ struct mft_inode *m = kzalloc(sizeof(struct mft_inode), GFP_NOFS); + + if (!m) + return -ENOMEM; + + err = mi_init(m, sbi, rno); + if (err) { +- ntfs_free(m); ++ kfree(m); + return err; + } + +@@ -100,14 +100,14 @@ int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi) + void mi_put(struct mft_inode *mi) + { + mi_clear(mi); +- ntfs_free(mi); ++ kfree(mi); + } + + int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno) + { + mi->sbi = sbi; + mi->rno = rno; +- mi->mrec = ntfs_malloc(sbi->record_size); ++ mi->mrec = kmalloc(sbi->record_size, GFP_NOFS); + if (!mi->mrec) + return -ENOMEM; + +diff --git a/src/run.c b/src/run.c +index 60c64deab7389261b9cb21f03118f2e8e2b8dc05..a9989f7536ba107621d15a2653183fb61fc45abc 100644 +--- a/src/run.c ++++ b/src/run.c +@@ -254,7 +254,7 @@ void run_truncate_head(struct runs_tree *run, CLST vcn) + run->count -= index; + + if (!run->count) { +- ntfs_vfree(run->runs); ++ kvfree(run->runs); + run->runs = NULL; + run->allocated = 0; + } +@@ -293,7 +293,7 @@ void run_truncate(struct runs_tree *run, CLST vcn) + + /* Do not reallocate array 'runs'. Only free if possible */ + if (!index) { +- ntfs_vfree(run->runs); ++ kvfree(run->runs); + run->runs = NULL; + run->allocated = 0; + } +@@ -388,7 +388,7 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + + WARN_ON(!is_mft && bytes > NTFS3_RUN_MAX_BYTES); + +- new_ptr = ntfs_vmalloc(bytes); ++ new_ptr = kvmalloc(bytes, GFP_KERNEL); + + if (!new_ptr) + return false; +@@ -399,7 +399,7 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + memcpy(r + 1, run->runs + index, + sizeof(struct ntfs_run) * (run->count - index)); + +- ntfs_vfree(run->runs); ++ kvfree(run->runs); + run->runs = new_ptr; + run->allocated = bytes; + +diff --git a/src/super.c b/src/super.c +index 7a501bca26d7916d23dc64d7919d4ad1384d5765..17ee715ab539ee6d7605256277c1f77c3132a6fa 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -468,9 +468,9 @@ static void init_once(void *foo) + /* noinline to reduce binary size*/ + static noinline void put_ntfs(struct ntfs_sb_info *sbi) + { +- ntfs_free(sbi->new_rec); +- ntfs_vfree(ntfs_put_shared(sbi->upcase)); +- ntfs_free(sbi->def_table); ++ kfree(sbi->new_rec); ++ kvfree(ntfs_put_shared(sbi->upcase)); ++ kfree(sbi->def_table); + + wnd_close(&sbi->mft.bitmap); + wnd_close(&sbi->used.bitmap); +@@ -496,14 +496,14 @@ static noinline void put_ntfs(struct ntfs_sb_info *sbi) + indx_clear(&sbi->security.index_sdh); + indx_clear(&sbi->reparse.index_r); + indx_clear(&sbi->objid.index_o); +- ntfs_free(sbi->compress.lznt); ++ kfree(sbi->compress.lznt); + #ifdef CONFIG_NTFS3_LZX_XPRESS + xpress_free_decompressor(sbi->compress.xpress); + lzx_free_decompressor(sbi->compress.lzx); + #endif + clear_mount_options(&sbi->options); + +- ntfs_free(sbi); ++ kfree(sbi); + } + + static void ntfs_put_super(struct super_block *sb) +@@ -848,7 +848,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + + sbi->used.bitmap.nbits = clusters; + +- rec = ntfs_zalloc(record_size); ++ rec = kzalloc(record_size, GFP_NOFS); + if (!rec) { + err = -ENOMEM; + goto out; +@@ -915,7 +915,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + ref.high = 0; + +- sbi = ntfs_zalloc(sizeof(struct ntfs_sb_info)); ++ sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS); + if (!sbi) + return -ENOMEM; + +@@ -1181,7 +1181,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + goto out; + } + bytes = inode->i_size; +- sbi->def_table = t = ntfs_malloc(bytes); ++ sbi->def_table = t = kmalloc(bytes, GFP_NOFS); + if (!t) { + err = -ENOMEM; + goto out; +@@ -1247,7 +1247,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + goto out; + } + +- sbi->upcase = upcase = ntfs_vmalloc(0x10000 * sizeof(short)); ++ sbi->upcase = upcase = kvmalloc(0x10000 * sizeof(short), GFP_KERNEL); + if (!upcase) { + err = -ENOMEM; + goto out; +@@ -1277,7 +1277,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + shared = ntfs_set_shared(upcase, 0x10000 * sizeof(short)); + if (shared && upcase != shared) { + sbi->upcase = shared; +- ntfs_vfree(upcase); ++ kvfree(upcase); + } + + iput(inode); +diff --git a/src/xattr.c b/src/xattr.c +index 6b17d46b9506ccaa6a6a06dfaf4dde00524186e2..af89e50f7b9f32926e4daaea9f8b729916519526 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -110,7 +110,7 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + return -EFBIG; + + /* Allocate memory for packed Ea */ +- ea_p = ntfs_malloc(size + add_bytes); ++ ea_p = kmalloc(size + add_bytes, GFP_NOFS); + if (!ea_p) + return -ENOMEM; + +@@ -142,7 +142,7 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + return 0; + + out: +- ntfs_free(ea_p); ++ kfree(ea_p); + *ea = NULL; + return err; + } +@@ -193,7 +193,7 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, + } + + out: +- ntfs_free(ea_all); ++ kfree(ea_all); + return err ? err : ret; + } + +@@ -251,7 +251,7 @@ static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, + err = 0; + + out: +- ntfs_free(ea_all); ++ kfree(ea_all); + if (!required) + ni_unlock(ni); + +@@ -352,7 +352,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + } + + if (!ea_all) { +- ea_all = ntfs_zalloc(add); ++ ea_all = kzalloc(add, GFP_NOFS); + if (!ea_all) { + err = -ENOMEM; + goto out; +@@ -474,7 +474,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + ni_unlock(ni); + + run_close(&ea_run); +- ntfs_free(ea_all); ++ kfree(ea_all); + + return err; + } +@@ -599,7 +599,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + value = NULL; + } else { + size = posix_acl_xattr_size(acl->a_count); +- value = ntfs_malloc(size); ++ value = kmalloc(size, GFP_NOFS); + if (!value) + return -ENOMEM; + +@@ -614,7 +614,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + set_cached_acl(inode, type, acl); + + out: +- ntfs_free(value); ++ kfree(value); + + return err; + } +@@ -880,7 +880,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, + err = sd_size; + memcpy(buffer, sd, sd_size); + } +- ntfs_free(sd); ++ kfree(sd); + goto out; + } + +-- +2.31.1 + diff --git a/SOURCES/0020-fs-ntfs3-Use-kcalloc-kmalloc_array-over-kzalloc-kmal.patch b/SOURCES/0020-fs-ntfs3-Use-kcalloc-kmalloc_array-over-kzalloc-kmal.patch new file mode 100644 index 0000000..646dc18 --- /dev/null +++ b/SOURCES/0020-fs-ntfs3-Use-kcalloc-kmalloc_array-over-kzalloc-kmal.patch @@ -0,0 +1,80 @@ +From 345482bc431f6492beb464696341626057f67771 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 24 Aug 2021 21:37:08 +0300 +Subject: [Backport 345482bc431f] src: Use kcalloc/kmalloc_array over + kzalloc/kmalloc + +Use kcalloc/kmalloc_array over kzalloc/kmalloc when we allocate array. +Checkpatch found these after we did not use our own defined allocation +wrappers. + +Reviewed-by: Christoph Hellwig +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/bitmap.c | 2 +- + src/file.c | 2 +- + src/frecord.c | 7 +++---- + 3 files changed, 5 insertions(+), 6 deletions(-) + +diff --git a/src/bitmap.c b/src/bitmap.c +index d502bba323d00e1ccafa7a0a0eeea46e0e36d796..2de05062c78b0aaa5799329aeb0065f0630f5f54 100644 +--- a/src/bitmap.c ++++ b/src/bitmap.c +@@ -683,7 +683,7 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits) + if (!wnd->bits_last) + wnd->bits_last = wbits; + +- wnd->free_bits = kzalloc(wnd->nwnd * sizeof(u16), GFP_NOFS); ++ wnd->free_bits = kcalloc(wnd->nwnd, sizeof(u16), GFP_NOFS); + if (!wnd->free_bits) + return -ENOMEM; + +diff --git a/src/file.c b/src/file.c +index 8d27c520bec56a1daca965fba7ba826049b18709..a959f6197c99e55275e12ffa78b9193f87bef942 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -900,7 +900,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + return -EOPNOTSUPP; + } + +- pages = kmalloc(pages_per_frame * sizeof(struct page *), GFP_NOFS); ++ pages = kmalloc_array(pages_per_frame, sizeof(struct page *), GFP_NOFS); + if (!pages) + return -ENOMEM; + +diff --git a/src/frecord.c b/src/frecord.c +index 2f7d165435307dc37dfbc6ba7959f04c1b218257..329bc76dfb09cb5d2a8e0215805d9a738c35bde6 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -2054,7 +2054,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + idx = (vbo - frame_vbo) >> PAGE_SHIFT; + + pages_per_frame = frame_size >> PAGE_SHIFT; +- pages = kzalloc(pages_per_frame * sizeof(struct page *), GFP_NOFS); ++ pages = kcalloc(pages_per_frame, sizeof(struct page *), GFP_NOFS); + if (!pages) { + err = -ENOMEM; + goto out; +@@ -2137,7 +2137,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + frame_bits = ni_ext_compress_bits(ni); + frame_size = 1u << frame_bits; + pages_per_frame = frame_size >> PAGE_SHIFT; +- pages = kzalloc(pages_per_frame * sizeof(struct page *), GFP_NOFS); ++ pages = kcalloc(pages_per_frame, sizeof(struct page *), GFP_NOFS); + if (!pages) { + err = -ENOMEM; + goto out; +@@ -2709,8 +2709,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + goto out; + } + +- pages_disk = kzalloc(pages_per_frame * sizeof(struct page *), +- GFP_NOFS); ++ pages_disk = kcalloc(pages_per_frame, sizeof(struct page *), GFP_NOFS); + if (!pages_disk) { + err = -ENOMEM; + goto out; +-- +2.31.1 + diff --git a/SOURCES/0021-fs-ntfs3-add-checks-for-allocation-failure.patch b/SOURCES/0021-fs-ntfs3-add-checks-for-allocation-failure.patch new file mode 100644 index 0000000..4a72eea --- /dev/null +++ b/SOURCES/0021-fs-ntfs3-add-checks-for-allocation-failure.patch @@ -0,0 +1,71 @@ +From a1b04d380ab64790a7b4a8eb52e14679e47065ab Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Tue, 24 Aug 2021 14:52:36 +0300 +Subject: [Backport a1b04d380ab6] src: add checks for allocation failure + +Add a check for when the kzalloc() in init_rsttbl() fails. Some of +the callers checked for NULL and some did not. I went down the call +tree and added NULL checks where ever they were missing. + +Fixes: b46acd6a6a62 ("src: Add NTFS journal") +Signed-off-by: Dan Carpenter +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/fslog.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/src/fslog.c b/src/fslog.c +index 2c213b55979ed06c03f4e74998fd03f9a9a8827b..7144ea8a9ab898e35c75e06192f20f9d93504bb8 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -809,6 +809,9 @@ static inline struct RESTART_TABLE *init_rsttbl(u16 esize, u16 used) + u32 lf = sizeof(struct RESTART_TABLE) + (used - 1) * esize; + struct RESTART_TABLE *t = kzalloc(bytes, GFP_NOFS); + ++ if (!t) ++ return NULL; ++ + t->size = cpu_to_le16(esize); + t->used = cpu_to_le16(used); + t->free_goal = cpu_to_le32(~0u); +@@ -831,7 +834,11 @@ static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl, + u16 esize = le16_to_cpu(tbl->size); + __le32 osize = cpu_to_le32(bytes_per_rt(tbl)); + u32 used = le16_to_cpu(tbl->used); +- struct RESTART_TABLE *rt = init_rsttbl(esize, used + add); ++ struct RESTART_TABLE *rt; ++ ++ rt = init_rsttbl(esize, used + add); ++ if (!rt) ++ return NULL; + + memcpy(rt + 1, tbl + 1, esize * used); + +@@ -864,8 +871,11 @@ static inline void *alloc_rsttbl_idx(struct RESTART_TABLE **tbl) + __le32 *e; + struct RESTART_TABLE *t = *tbl; + +- if (!t->first_free) ++ if (!t->first_free) { + *tbl = t = extend_rsttbl(t, 16, ~0u); ++ if (!t) ++ return NULL; ++ } + + off = le32_to_cpu(t->first_free); + +@@ -4482,6 +4492,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + } + + dp = alloc_rsttbl_idx(&dptbl); ++ if (!dp) { ++ err = -ENOMEM; ++ goto out; ++ } + dp->target_attr = cpu_to_le32(t16); + dp->transfer_len = cpu_to_le32(t32 << sbi->cluster_bits); + dp->lcns_follow = cpu_to_le32(t32); +-- +2.31.1 + diff --git a/SOURCES/0022-fs-ntfs3-fix-an-error-code-in-ntfs_get_acl_ex.patch b/SOURCES/0022-fs-ntfs3-fix-an-error-code-in-ntfs_get_acl_ex.patch new file mode 100644 index 0000000..cebc259 --- /dev/null +++ b/SOURCES/0022-fs-ntfs3-fix-an-error-code-in-ntfs_get_acl_ex.patch @@ -0,0 +1,34 @@ +From 2926e4297053c735ab65450192dfba32a4f47fa9 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Tue, 24 Aug 2021 14:48:58 +0300 +Subject: [Backport 2926e4297053] src: fix an error code in + ntfs_get_acl_ex() + +The ntfs_get_ea() function returns negative error codes or on success +it returns the length. In the original code a zero length return was +treated as -ENODATA and results in a NULL return. But it should be +treated as an invalid length and result in an PTR_ERR(-EINVAL) return. + +Fixes: be71b5cba2e6 ("src: Add attrib operations") +Signed-off-by: Dan Carpenter +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/xattr.c b/src/xattr.c +index af89e50f7b9f32926e4daaea9f8b729916519526..d3d5b9d331d1f55010843085738cf395b640eb0b 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -521,7 +521,7 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + ni_unlock(ni); + + /* Translate extended attribute to acl */ +- if (err > 0) { ++ if (err >= 0) { + acl = posix_acl_from_xattr(mnt_userns, buf, err); + if (!IS_ERR(acl)) + set_cached_acl(inode, type, acl); +-- +2.31.1 + diff --git a/SOURCES/0023-fs-ntfs3-Fix-error-code-in-indx_add_allocate.patch b/SOURCES/0023-fs-ntfs3-Fix-error-code-in-indx_add_allocate.patch new file mode 100644 index 0000000..733affc --- /dev/null +++ b/SOURCES/0023-fs-ntfs3-Fix-error-code-in-indx_add_allocate.patch @@ -0,0 +1,31 @@ +From 04810f000afdbdd37825ca7f563f036119422cb7 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Tue, 24 Aug 2021 10:49:32 +0300 +Subject: [Backport 04810f000afd] src: Fix error code in + indx_add_allocate() + +Return -EINVAL if ni_find_attr() fails. Don't return success. + +Fixes: 82cae269cfa9 ("src: Add initialization of super block") +Signed-off-by: Dan Carpenter +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/index.c b/src/index.c +index 334a3cef714b3e5c5b6fc31b3fd33d39bb5caa25..5fb41c9c89100aee4d6d0e8f7cf65556aec11250 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -1500,6 +1500,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + alloc = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, in->name, in->name_len, + NULL, &mi); + if (!alloc) { ++ err = -EINVAL; + if (bmp) + goto out2; + goto out1; +-- +2.31.1 + diff --git a/SOURCES/0024-fs-ntfs3-Potential-NULL-dereference-in-hdr_find_spli.patch b/SOURCES/0024-fs-ntfs3-Potential-NULL-dereference-in-hdr_find_spli.patch new file mode 100644 index 0000000..092517b --- /dev/null +++ b/SOURCES/0024-fs-ntfs3-Potential-NULL-dereference-in-hdr_find_spli.patch @@ -0,0 +1,38 @@ +From 8c83a4851da1c7eda83098ade238665b15774da3 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Tue, 24 Aug 2021 10:50:15 +0300 +Subject: [Backport 8c83a4851da1] src: Potential NULL dereference in + hdr_find_split() + +The "e" pointer is dereferenced before it has been checked for NULL. +Move the dereference after the NULL check to prevent an Oops. + +Fixes: 82cae269cfa9 ("src: Add initialization of super block") +Signed-off-by: Dan Carpenter +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/index.c b/src/index.c +index 5fb41c9c89100aee4d6d0e8f7cf65556aec11250..f4729aa50671f27de961305744b3839588696108 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -557,11 +557,12 @@ static const struct NTFS_DE *hdr_find_split(const struct INDEX_HDR *hdr) + size_t o; + const struct NTFS_DE *e = hdr_first_de(hdr); + u32 used_2 = le32_to_cpu(hdr->used) >> 1; +- u16 esize = le16_to_cpu(e->size); ++ u16 esize; + + if (!e || de_is_last(e)) + return NULL; + ++ esize = le16_to_cpu(e->size); + for (o = le32_to_cpu(hdr->de_off) + esize; o < used_2; o += esize) { + const struct NTFS_DE *p = e; + +-- +2.31.1 + diff --git a/SOURCES/0025-fs-ntfs3-Fix-error-handling-in-indx_insert_into_root.patch b/SOURCES/0025-fs-ntfs3-Fix-error-handling-in-indx_insert_into_root.patch new file mode 100644 index 0000000..dd18d84 --- /dev/null +++ b/SOURCES/0025-fs-ntfs3-Fix-error-handling-in-indx_insert_into_root.patch @@ -0,0 +1,154 @@ +From b8155e95de38b25a69dfb03e4731fd6c5a28531e Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Tue, 24 Aug 2021 10:51:04 +0300 +Subject: [Backport b8155e95de38] src: Fix error handling in + indx_insert_into_root() + +There are three bugs in this code: +1) If indx_get_root() fails, then return -EINVAL instead of success. +2) On the "/* make root external */" -EOPNOTSUPP; error path it should + free "re" but it has a memory leak. +3) If indx_new() fails then it will lead to an error pointer dereference + when we call put_indx_node(). + +I've re-written the error handling to be more clear. + +Fixes: 82cae269cfa9 ("src: Add initialization of super block") +Signed-off-by: Dan Carpenter +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 36 ++++++++++++++++-------------------- + 1 file changed, 16 insertions(+), 20 deletions(-) + +diff --git a/src/index.c b/src/index.c +index f4729aa50671f27de961305744b3839588696108..69c6c4e0b4d9bd76b79de148f164fec98b7fac9f 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -1555,12 +1555,12 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + u32 root_size, new_root_size; + struct ntfs_sb_info *sbi; + int ds_root; +- struct INDEX_ROOT *root, *a_root = NULL; ++ struct INDEX_ROOT *root, *a_root; + + /* Get the record this root placed in */ + root = indx_get_root(indx, ni, &attr, &mi); + if (!root) +- goto out; ++ return -EINVAL; + + /* + * Try easy case: +@@ -1592,10 +1592,8 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + + /* Make a copy of root attribute to restore if error */ + a_root = kmemdup(attr, asize, GFP_NOFS); +- if (!a_root) { +- err = -ENOMEM; +- goto out; +- } ++ if (!a_root) ++ return -ENOMEM; + + /* copy all the non-end entries from the index root to the new buffer.*/ + to_move = 0; +@@ -1605,7 +1603,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + for (e = e0;; e = hdr_next_de(hdr, e)) { + if (!e) { + err = -EINVAL; +- goto out; ++ goto out_free_root; + } + + if (de_is_last(e)) +@@ -1613,14 +1611,13 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + to_move += le16_to_cpu(e->size); + } + +- n = NULL; + if (!to_move) { + re = NULL; + } else { + re = kmemdup(e0, to_move, GFP_NOFS); + if (!re) { + err = -ENOMEM; +- goto out; ++ goto out_free_root; + } + } + +@@ -1637,7 +1634,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + if (ds_root > 0 && used + ds_root > sbi->max_bytes_per_attr) { + /* make root external */ + err = -EOPNOTSUPP; +- goto out; ++ goto out_free_re; + } + + if (ds_root) +@@ -1667,7 +1664,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + /* bug? */ + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + err = -EINVAL; +- goto out1; ++ goto out_free_re; + } + + if (err) { +@@ -1678,7 +1675,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + /* bug? */ + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + } +- goto out1; ++ goto out_free_re; + } + + e = (struct NTFS_DE *)(root + 1); +@@ -1689,7 +1686,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + n = indx_new(indx, ni, new_vbn, sub_vbn); + if (IS_ERR(n)) { + err = PTR_ERR(n); +- goto out1; ++ goto out_free_re; + } + + hdr = &n->index->ihdr; +@@ -1716,7 +1713,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + put_indx_node(n); + fnd_clear(fnd); + err = indx_insert_entry(indx, ni, new_de, ctx, fnd); +- goto out; ++ goto out_free_root; + } + + /* +@@ -1726,7 +1723,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + e = hdr_insert_de(indx, hdr, new_de, NULL, ctx); + if (!e) { + err = -EINVAL; +- goto out1; ++ goto out_put_n; + } + fnd_push(fnd, n, e); + +@@ -1735,12 +1732,11 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + + n = NULL; + +-out1: ++out_put_n: ++ put_indx_node(n); ++out_free_re: + kfree(re); +- if (n) +- put_indx_node(n); +- +-out: ++out_free_root: + kfree(a_root); + return err; + } +-- +2.31.1 + diff --git a/SOURCES/0026-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch b/SOURCES/0026-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch new file mode 100644 index 0000000..575ca44 --- /dev/null +++ b/SOURCES/0026-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch @@ -0,0 +1,12222 @@ +From e8b8e97f91b80f08a2f1b7ea4f81e7af61b2cc2f Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 3 Aug 2021 14:57:09 +0300 +Subject: [Backport e8b8e97f91b8] src: Restyle comments to better align + with kernel-doc + +Capitalize comments and end with period for better reading. + +Also function comments are now little more kernel-doc style. This way we +can easily convert them to kernel-doc style if we want. Note that these +are not yet complete with this style. Example function comments start +with /* and in kernel-doc style they start /**. + +Use imperative mood in function descriptions. + +Change words like ntfs -> NTFS, linux -> Linux. + +Use "we" not "I" when commenting code. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 230 +++++++------- + src/attrlist.c | 62 ++-- + src/bitfunc.c | 7 +- + src/bitmap.c | 201 ++++++------ + src/debug.h | 5 +- + src/dir.c | 61 ++-- + src/file.c | 96 +++--- + src/frecord.c | 409 ++++++++++++------------- + src/fslog.c | 728 +++++++++++++++++++++++--------------------- + src/fsntfs.c | 244 +++++++-------- + src/index.c | 395 ++++++++++++------------ + src/inode.c | 229 +++++++------- + src/lznt.c | 86 +++--- + src/namei.c | 66 ++-- + src/ntfs.h | 511 +++++++++++++++---------------- + src/ntfs_fs.h | 196 ++++++------ + src/record.c | 60 ++-- + src/run.c | 187 ++++++------ + src/super.c | 160 +++++----- + src/upcase.c | 13 +- + src/xattr.c | 111 +++---- + 21 files changed, 1987 insertions(+), 2070 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index 4eae9886e27d0f481671c4b40c22a3742d794e4f..4b285f704e621ef5cbba11f1a006622e6387ea09 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -3,7 +3,7 @@ + * + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * +- * TODO: merge attr_set_size/attr_data_get_block/attr_allocate_frame? ++ * TODO: Merge attr_set_size/attr_data_get_block/attr_allocate_frame? + */ + + #include +@@ -20,7 +20,7 @@ + + /* + * You can set external NTFS_MIN_LOG2_OF_CLUMP/NTFS_MAX_LOG2_OF_CLUMP to manage +- * preallocate algorithm ++ * preallocate algorithm. + */ + #ifndef NTFS_MIN_LOG2_OF_CLUMP + #define NTFS_MIN_LOG2_OF_CLUMP 16 +@@ -35,10 +35,6 @@ + // 16G + #define NTFS_CLUMP_MAX (1ull << (NTFS_MAX_LOG2_OF_CLUMP + 8)) + +-/* +- * get_pre_allocated +- * +- */ + static inline u64 get_pre_allocated(u64 size) + { + u32 clump; +@@ -65,7 +61,7 @@ static inline u64 get_pre_allocated(u64 size) + /* + * attr_must_be_resident + * +- * returns true if attribute must be resident ++ * Return: True if attribute must be resident. + */ + static inline bool attr_must_be_resident(struct ntfs_sb_info *sbi, + enum ATTR_TYPE type) +@@ -90,9 +86,7 @@ static inline bool attr_must_be_resident(struct ntfs_sb_info *sbi, + } + + /* +- * attr_load_runs +- * +- * load all runs stored in 'attr' ++ * attr_load_runs - Load all runs stored in @attr. + */ + int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni, + struct runs_tree *run, const CLST *vcn) +@@ -121,9 +115,7 @@ int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni, + } + + /* +- * int run_deallocate_ex +- * +- * Deallocate clusters ++ * run_deallocate_ex - Deallocate clusters. + */ + static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run, + CLST vcn, CLST len, CLST *done, bool trim) +@@ -163,7 +155,7 @@ static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run, + vcn_next = vcn + clen; + if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || + vcn != vcn_next) { +- // save memory - don't load entire run ++ /* Save memory - don't load entire run. */ + goto failed; + } + } +@@ -176,9 +168,7 @@ static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run, + } + + /* +- * attr_allocate_clusters +- * +- * find free space, mark it as used and store in 'run' ++ * attr_allocate_clusters - Find free space, mark it as used and store in @run. + */ + int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, + CLST vcn, CLST lcn, CLST len, CLST *pre_alloc, +@@ -207,7 +197,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, + if (new_lcn && vcn == vcn0) + *new_lcn = lcn; + +- /* Add new fragment into run storage */ ++ /* Add new fragment into run storage. */ + if (!run_add_entry(run, vcn, lcn, flen, opt == ALLOCATE_MFT)) { + down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); + wnd_set_free(wnd, lcn, flen); +@@ -228,7 +218,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, + } + + out: +- /* undo */ ++ /* Undo. */ + run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false); + run_truncate(run, vcn0); + +@@ -236,8 +226,10 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, + } + + /* +- * if page is not NULL - it is already contains resident data +- * and locked (called from ni_write_frame) ++ * attr_make_nonresident ++ * ++ * If page is not NULL - it is already contains resident data ++ * and locked (called from ni_write_frame()). + */ + int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + struct ATTR_LIST_ENTRY *le, struct mft_inode *mi, +@@ -275,7 +267,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + + run_init(run); + +- /* make a copy of original attribute */ ++ /* Make a copy of original attribute. */ + attr_s = kmemdup(attr, asize, GFP_NOFS); + if (!attr_s) { + err = -ENOMEM; +@@ -283,7 +275,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + } + + if (!len) { +- /* empty resident -> empty nonresident */ ++ /* Empty resident -> Empty nonresident. */ + alen = 0; + } else { + const char *data = resident_data(attr); +@@ -294,7 +286,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + goto out1; + + if (!rsize) { +- /* empty resident -> non empty nonresident */ ++ /* Empty resident -> Non empty nonresident. */ + } else if (!is_data) { + err = ntfs_sb_write_run(sbi, run, 0, data, rsize); + if (err) +@@ -319,7 +311,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + } + } + +- /* remove original attribute */ ++ /* Remove original attribute. */ + used -= asize; + memmove(attr, Add2Ptr(attr, asize), used - aoff); + rec->used = cpu_to_le32(used); +@@ -342,7 +334,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + if (is_data) + ni->ni_flags &= ~NI_FLAG_RESIDENT; + +- /* Resident attribute becomes non resident */ ++ /* Resident attribute becomes non resident. */ + return 0; + + out3: +@@ -352,20 +344,18 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + rec->used = cpu_to_le32(used + asize); + mi->dirty = true; + out2: +- /* undo: do not trim new allocated clusters */ ++ /* Undo: do not trim new allocated clusters. */ + run_deallocate(sbi, run, false); + run_close(run); + out1: + kfree(attr_s); +- /*reinsert le*/ ++ /* Reinsert le. */ + out: + return err; + } + + /* +- * attr_set_size_res +- * +- * helper for attr_set_size ++ * attr_set_size_res - Helper for attr_set_size(). + */ + static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr, + struct ATTR_LIST_ENTRY *le, struct mft_inode *mi, +@@ -407,14 +397,13 @@ static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr, + } + + /* +- * attr_set_size ++ * attr_set_size - Change the size of attribute. + * +- * change the size of attribute + * Extend: +- * - sparse/compressed: no allocated clusters +- * - normal: append allocated and preallocated new clusters ++ * - Sparse/compressed: No allocated clusters. ++ * - Normal: Append allocated and preallocated new clusters. + * Shrink: +- * - no deallocate if keep_prealloc is set ++ * - No deallocate if @keep_prealloc is set. + */ + int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, struct runs_tree *run, +@@ -451,7 +440,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (err || !attr_b->non_res) + goto out; + +- /* layout of records may be changed, so do a full search */ ++ /* Layout of records may be changed, so do a full search. */ + goto again; + } + +@@ -530,10 +519,10 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + add_alloc_in_same_attr_seg: + lcn = 0; + if (is_mft) { +- /* mft allocates clusters from mftzone */ ++ /* MFT allocates clusters from MFT zone. */ + pre_alloc = 0; + } else if (is_ext) { +- /* no preallocate for sparse/compress */ ++ /* No preallocate for sparse/compress. */ + pre_alloc = 0; + } else if (pre_alloc == -1) { + pre_alloc = 0; +@@ -544,7 +533,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + pre_alloc = new_alen2 - new_alen; + } + +- /* Get the last lcn to allocate from */ ++ /* Get the last LCN to allocate from. */ + if (old_alen && + !run_lookup_entry(run, vcn, &lcn, NULL, NULL)) { + lcn = SPARSE_LCN; +@@ -575,7 +564,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + alen = to_allocate; + } else { +- /* ~3 bytes per fragment */ ++ /* ~3 bytes per fragment. */ + err = attr_allocate_clusters( + sbi, run, vcn, lcn, to_allocate, &pre_alloc, + is_mft ? ALLOCATE_MFT : 0, &alen, +@@ -607,12 +596,12 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + mi_b->dirty = true; + + if (next_svcn >= vcn && !to_allocate) { +- /* Normal way. update attribute and exit */ ++ /* Normal way. Update attribute and exit. */ + attr_b->nres.data_size = cpu_to_le64(new_size); + goto ok; + } + +- /* at least two mft to avoid recursive loop*/ ++ /* At least two MFT to avoid recursive loop. */ + if (is_mft && next_svcn == vcn && + ((u64)done << sbi->cluster_bits) >= 2 * sbi->record_size) { + new_size = new_alloc_tmp; +@@ -637,7 +626,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (next_svcn < vcn) + goto pack_runs; + +- /* layout of records is changed */ ++ /* Layout of records is changed. */ + goto again; + } + +@@ -645,15 +634,15 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + err = ni_create_attr_list(ni); + if (err) + goto out; +- /* layout of records is changed */ ++ /* Layout of records is changed. */ + } + + if (next_svcn >= vcn) { +- /* this is mft data, repeat */ ++ /* This is MFT data, repeat. */ + goto again; + } + +- /* insert new attribute segment */ ++ /* Insert new attribute segment. */ + err = ni_insert_nonresident(ni, type, name, name_len, run, + next_svcn, vcn - next_svcn, + attr_b->flags, &attr, &mi); +@@ -667,8 +656,10 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + evcn = le64_to_cpu(attr->nres.evcn); + + le_b = NULL; +- /* layout of records maybe changed */ +- /* find base attribute to update*/ ++ /* ++ * Layout of records maybe changed. ++ * Find base attribute to update. ++ */ + attr_b = ni_find_attr(ni, NULL, &le_b, type, name, name_len, + NULL, &mi_b); + if (!attr_b) { +@@ -704,11 +695,11 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + u16 le_sz = le16_to_cpu(le->size); + + /* +- * NOTE: list entries for one attribute are always ++ * NOTE: List entries for one attribute are always + * the same size. We deal with last entry (vcn==0) + * and it is not first in entries array +- * (list entry for std attribute always first) +- * So it is safe to step back ++ * (list entry for std attribute always first). ++ * So it is safe to step back. + */ + mi_remove_attr(mi, attr); + +@@ -793,7 +784,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (!err && attr_b && ret) + *ret = attr_b; + +- /* update inode_set_bytes*/ ++ /* Update inode_set_bytes. */ + if (!err && ((type == ATTR_DATA && !name_len) || + (type == ATTR_ALLOC && name == I30_NAME))) { + bool dirty = false; +@@ -843,7 +834,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + up_read(&ni->file.run_lock); + + if (ok && (*lcn != SPARSE_LCN || !new)) { +- /* normal way */ ++ /* Normal way. */ + return 0; + } + +@@ -909,7 +900,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + if (!ok) { + ok = run_lookup_entry(run, vcn, lcn, len, NULL); + if (ok && (*lcn != SPARSE_LCN || !new)) { +- /* normal way */ ++ /* Normal way. */ + err = 0; + goto ok; + } +@@ -932,7 +923,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + goto out; + } + +- /* Get the last lcn to allocate from */ ++ /* Get the last LCN to allocate from. */ + hint = 0; + + if (vcn > evcn1) { +@@ -970,20 +961,20 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + mi_b->dirty = true; + mark_inode_dirty(&ni->vfs_inode); + +- /* stored [vcn : next_svcn) from [vcn : end) */ ++ /* Stored [vcn : next_svcn) from [vcn : end). */ + next_svcn = le64_to_cpu(attr->nres.evcn) + 1; + + if (end <= evcn1) { + if (next_svcn == evcn1) { +- /* Normal way. update attribute and exit */ ++ /* Normal way. Update attribute and exit. */ + goto ok; + } +- /* add new segment [next_svcn : evcn1 - next_svcn )*/ ++ /* Add new segment [next_svcn : evcn1 - next_svcn). */ + if (!ni->attr_list.size) { + err = ni_create_attr_list(ni); + if (err) + goto out; +- /* layout of records is changed */ ++ /* Layout of records is changed. */ + le_b = NULL; + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, + 0, NULL, &mi_b); +@@ -1001,7 +992,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + + svcn = evcn1; + +- /* Estimate next attribute */ ++ /* Estimate next attribute. */ + attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); + + if (attr) { +@@ -1012,7 +1003,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + if (end < next_svcn) + end = next_svcn; + while (end > evcn) { +- /* remove segment [svcn : evcn)*/ ++ /* Remove segment [svcn : evcn). */ + mi_remove_attr(mi, attr); + + if (!al_remove_le(ni, le)) { +@@ -1021,7 +1012,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + } + + if (evcn + 1 >= alloc) { +- /* last attribute segment */ ++ /* Last attribute segment. */ + evcn1 = evcn + 1; + goto ins_ext; + } +@@ -1125,7 +1116,7 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct page *page) + return -EINVAL; + + if (attr->non_res) { +- /*return special error code to check this case*/ ++ /* Return special error code to check this case. */ + return E_NTFS_NONRESIDENT; + } + +@@ -1148,9 +1139,7 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct page *page) + } + + /* +- * attr_load_runs_vcn +- * +- * load runs with vcn ++ * attr_load_runs_vcn - Load runs with VCN. + */ + int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, struct runs_tree *run, +@@ -1180,7 +1169,7 @@ int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + /* +- * load runs for given range [from to) ++ * attr_wof_load_runs_range - Load runs for given range [from to). + */ + int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, struct runs_tree *run, +@@ -1199,7 +1188,7 @@ int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type, + vcn); + if (err) + return err; +- clen = 0; /*next run_lookup_entry(vcn) must be success*/ ++ clen = 0; /* Next run_lookup_entry(vcn) must be success. */ + } + } + +@@ -1210,7 +1199,7 @@ int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type, + /* + * attr_wof_frame_info + * +- * read header of xpress/lzx file to get info about frame ++ * Read header of Xpress/LZX file to get info about frame. + */ + int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, + struct runs_tree *run, u64 frame, u64 frames, +@@ -1227,20 +1216,20 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, + __le64 *off64; + + if (ni->vfs_inode.i_size < 0x100000000ull) { +- /* file starts with array of 32 bit offsets */ ++ /* File starts with array of 32 bit offsets. */ + bytes_per_off = sizeof(__le32); + vbo[1] = frame << 2; + *vbo_data = frames << 2; + } else { +- /* file starts with array of 64 bit offsets */ ++ /* File starts with array of 64 bit offsets. */ + bytes_per_off = sizeof(__le64); + vbo[1] = frame << 3; + *vbo_data = frames << 3; + } + + /* +- * read 4/8 bytes at [vbo - 4(8)] == offset where compressed frame starts +- * read 4/8 bytes at [vbo] == offset where compressed frame ends ++ * Read 4/8 bytes at [vbo - 4(8)] == offset where compressed frame starts. ++ * Read 4/8 bytes at [vbo] == offset where compressed frame ends. + */ + if (!attr->non_res) { + if (vbo[1] + bytes_per_off > le32_to_cpu(attr->res.data_size)) { +@@ -1329,7 +1318,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, + off[0] = le64_to_cpu(*off64); + } + } else { +- /* two values in one page*/ ++ /* Two values in one page. */ + if (bytes_per_off == sizeof(__le32)) { + off32 = Add2Ptr(addr, voff); + off[0] = le32_to_cpu(off32[-1]); +@@ -1355,9 +1344,7 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr, + #endif + + /* +- * attr_is_frame_compressed +- * +- * This function is used to detect compressed frame ++ * attr_is_frame_compressed - Used to detect compressed frame. + */ + int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, + CLST frame, CLST *clst_data) +@@ -1391,14 +1378,14 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, + } + + if (lcn == SPARSE_LCN) { +- /* sparsed frame */ ++ /* Sparsed frame. */ + return 0; + } + + if (clen >= clst_frame) { + /* + * The frame is not compressed 'cause +- * it does not contain any sparse clusters ++ * it does not contain any sparse clusters. + */ + *clst_data = clst_frame; + return 0; +@@ -1409,8 +1396,8 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, + *clst_data = clen; + + /* +- * The frame is compressed if *clst_data + slen >= clst_frame +- * Check next fragments ++ * The frame is compressed if *clst_data + slen >= clst_frame. ++ * Check next fragments. + */ + while ((vcn += clen) < alen) { + vcn_next = vcn; +@@ -1433,8 +1420,8 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, + } else { + if (slen) { + /* +- * data_clusters + sparse_clusters = +- * not enough for frame ++ * Data_clusters + sparse_clusters = ++ * not enough for frame. + */ + return -EINVAL; + } +@@ -1445,11 +1432,11 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, + if (!slen) { + /* + * There is no sparsed clusters in this frame +- * So it is not compressed ++ * so it is not compressed. + */ + *clst_data = clst_frame; + } else { +- /*frame is compressed*/ ++ /* Frame is compressed. */ + } + break; + } +@@ -1459,10 +1446,9 @@ int attr_is_frame_compressed(struct ntfs_inode *ni, struct ATTRIB *attr, + } + + /* +- * attr_allocate_frame ++ * attr_allocate_frame - Allocate/free clusters for @frame. + * +- * allocate/free clusters for 'frame' +- * assumed: down_write(&ni->file.run_lock); ++ * Assumed: down_write(&ni->file.run_lock); + */ + int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + u64 new_valid) +@@ -1538,10 +1524,10 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + goto out; + } + end = vcn + clst_data; +- /* run contains updated range [vcn + len : end) */ ++ /* Run contains updated range [vcn + len : end). */ + } else { + CLST alen, hint = 0; +- /* Get the last lcn to allocate from */ ++ /* Get the last LCN to allocate from. */ + if (vcn + clst_data && + !run_lookup_entry(run, vcn + clst_data - 1, &hint, NULL, + NULL)) { +@@ -1555,7 +1541,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + goto out; + + end = vcn + len; +- /* run contains updated range [vcn + clst_data : end) */ ++ /* Run contains updated range [vcn + clst_data : end). */ + } + + total_size += (u64)len << sbi->cluster_bits; +@@ -1571,20 +1557,20 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + mi_b->dirty = true; + mark_inode_dirty(&ni->vfs_inode); + +- /* stored [vcn : next_svcn) from [vcn : end) */ ++ /* Stored [vcn : next_svcn) from [vcn : end). */ + next_svcn = le64_to_cpu(attr->nres.evcn) + 1; + + if (end <= evcn1) { + if (next_svcn == evcn1) { +- /* Normal way. update attribute and exit */ ++ /* Normal way. Update attribute and exit. */ + goto ok; + } +- /* add new segment [next_svcn : evcn1 - next_svcn )*/ ++ /* Add new segment [next_svcn : evcn1 - next_svcn). */ + if (!ni->attr_list.size) { + err = ni_create_attr_list(ni); + if (err) + goto out; +- /* layout of records is changed */ ++ /* Layout of records is changed. */ + le_b = NULL; + attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, + 0, NULL, &mi_b); +@@ -1602,7 +1588,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + + svcn = evcn1; + +- /* Estimate next attribute */ ++ /* Estimate next attribute. */ + attr = ni_find_attr(ni, attr, &le, ATTR_DATA, NULL, 0, &svcn, &mi); + + if (attr) { +@@ -1613,7 +1599,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + if (end < next_svcn) + end = next_svcn; + while (end > evcn) { +- /* remove segment [svcn : evcn)*/ ++ /* Remove segment [svcn : evcn). */ + mi_remove_attr(mi, attr); + + if (!al_remove_le(ni, le)) { +@@ -1622,7 +1608,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + } + + if (evcn + 1 >= alloc) { +- /* last attribute segment */ ++ /* Last attribute segment. */ + evcn1 = evcn + 1; + goto ins_ext; + } +@@ -1684,7 +1670,9 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + return err; + } + +-/* Collapse range in file */ ++/* ++ * attr_collapse_range - Collapse range in file. ++ */ + int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + { + int err = 0; +@@ -1725,7 +1713,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + } + + if ((vbo & mask) || (bytes & mask)) { +- /* allow to collapse only cluster aligned ranges */ ++ /* Allow to collapse only cluster aligned ranges. */ + return -EINVAL; + } + +@@ -1737,7 +1725,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + if (vbo + bytes >= data_size) { + u64 new_valid = min(ni->i_valid, vbo); + +- /* Simple truncate file at 'vbo' */ ++ /* Simple truncate file at 'vbo'. */ + truncate_setsize(&ni->vfs_inode, vbo); + err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, vbo, + &new_valid, true, NULL); +@@ -1749,7 +1737,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + } + + /* +- * Enumerate all attribute segments and collapse ++ * Enumerate all attribute segments and collapse. + */ + alen = alloc_size >> sbi->cluster_bits; + vcn = vbo >> sbi->cluster_bits; +@@ -1782,7 +1770,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + + for (;;) { + if (svcn >= end) { +- /* shift vcn */ ++ /* Shift VCN- */ + attr->nres.svcn = cpu_to_le64(svcn - len); + attr->nres.evcn = cpu_to_le64(evcn1 - 1 - len); + if (le) { +@@ -1793,7 +1781,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + } else if (svcn < vcn || end < evcn1) { + CLST vcn1, eat, next_svcn; + +- /* collapse a part of this attribute segment */ ++ /* Collapse a part of this attribute segment. */ + err = attr_load_runs(attr, ni, run, &svcn); + if (err) + goto out; +@@ -1811,7 +1799,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + } + + if (svcn >= vcn) { +- /* shift vcn */ ++ /* Shift VCN */ + attr->nres.svcn = cpu_to_le64(vcn); + if (le) { + le->vcn = attr->nres.svcn; +@@ -1832,7 +1820,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + if (err) + goto out; + +- /* layout of records maybe changed */ ++ /* Layout of records maybe changed. */ + attr_b = NULL; + le = al_find_ex(ni, NULL, ATTR_DATA, NULL, 0, + &next_svcn); +@@ -1842,18 +1830,18 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + } + } + +- /* free all allocated memory */ ++ /* Free all allocated memory. */ + run_truncate(run, 0); + } else { + u16 le_sz; + u16 roff = le16_to_cpu(attr->nres.run_off); + +- /*run==1 means unpack and deallocate*/ ++ /* run==1 means unpack and deallocate. */ + run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, + evcn1 - 1, svcn, Add2Ptr(attr, roff), + le32_to_cpu(attr->size) - roff); + +- /* delete this attribute segment */ ++ /* Delete this attribute segment. */ + mi_remove_attr(mi, attr); + if (!le) + break; +@@ -1868,13 +1856,13 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + break; + + if (!svcn) { +- /* Load next record that contains this attribute */ ++ /* Load next record that contains this attribute. */ + if (ni_load_mi(ni, le, &mi)) { + err = -EINVAL; + goto out; + } + +- /* Look for required attribute */ ++ /* Look for required attribute. */ + attr = mi_find_attr(mi, NULL, ATTR_DATA, NULL, + 0, &le->id); + if (!attr) { +@@ -1925,7 +1913,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + attr_b->nres.total_size = cpu_to_le64(total_size); + mi_b->dirty = true; + +- /*update inode size*/ ++ /* Update inode size. */ + ni->i_valid = valid_size; + ni->vfs_inode.i_size = data_size; + inode_set_bytes(&ni->vfs_inode, total_size); +@@ -1940,7 +1928,11 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + return err; + } + +-/* not for normal files */ ++/* ++ * attr_punch_hole ++ * ++ * Not for normal files. ++ */ + int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + { + int err = 0; +@@ -1981,7 +1973,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + total_size = le64_to_cpu(attr_b->nres.total_size); + + if (vbo >= alloc_size) { +- // NOTE: it is allowed ++ // NOTE: It is allowed. + return 0; + } + +@@ -2004,7 +1996,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + + down_write(&ni->file.run_lock); + /* +- * Enumerate all attribute segments and punch hole where necessary ++ * Enumerate all attribute segments and punch hole where necessary. + */ + alen = alloc_size >> sbi->cluster_bits; + vcn = vbo >> sbi->cluster_bits; +@@ -2050,7 +2042,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + goto out; + + if (dealloc2 == dealloc) { +- /* looks like the required range is already sparsed */ ++ /* Looks like the required range is already sparsed. */ + } else { + if (!run_add_entry(run, vcn1, SPARSE_LCN, zero, + false)) { +@@ -2062,7 +2054,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + if (err) + goto out; + } +- /* free all allocated memory */ ++ /* Free all allocated memory. */ + run_truncate(run, 0); + + if (evcn1 >= alen) +@@ -2082,7 +2074,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + attr_b->nres.total_size = cpu_to_le64(total_size); + mi_b->dirty = true; + +- /*update inode size*/ ++ /* Update inode size. */ + inode_set_bytes(&ni->vfs_inode, total_size); + ni->ni_flags |= NI_FLAG_UPDATE_PARENT; + mark_inode_dirty(&ni->vfs_inode); +diff --git a/src/attrlist.c b/src/attrlist.c +index 348bfb54db09f364f64182f51468f6cceb12e37d..32ca990af64b53b4e9572d5dd0d6ae5f057760c4 100644 +--- a/src/attrlist.c ++++ b/src/attrlist.c +@@ -14,7 +14,11 @@ + #include "ntfs.h" + #include "ntfs_fs.h" + +-/* Returns true if le is valid */ ++/* ++ * al_is_valid_le ++ * ++ * Return: True if @le is valid. ++ */ + static inline bool al_is_valid_le(const struct ntfs_inode *ni, + struct ATTR_LIST_ENTRY *le) + { +@@ -101,8 +105,9 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) + /* + * al_enumerate + * +- * Returns the next list 'le' +- * if 'le' is NULL then returns the first 'le' ++ * Return: ++ * * The next list le. ++ * * If @le is NULL then return the first le. + */ + struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, + struct ATTR_LIST_ENTRY *le) +@@ -115,22 +120,22 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, + } else { + sz = le16_to_cpu(le->size); + if (sz < sizeof(struct ATTR_LIST_ENTRY)) { +- /* Impossible 'cause we should not return such 'le' */ ++ /* Impossible 'cause we should not return such le. */ + return NULL; + } + le = Add2Ptr(le, sz); + } + +- /* Check boundary */ ++ /* Check boundary. */ + off = PtrOffset(ni->attr_list.le, le); + if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) { +- // The regular end of list ++ /* The regular end of list. */ + return NULL; + } + + sz = le16_to_cpu(le->size); + +- /* Check 'le' for errors */ ++ /* Check le for errors. */ + if (sz < sizeof(struct ATTR_LIST_ENTRY) || + off + sz > ni->attr_list.size || + sz < le->name_off + le->name_len * sizeof(short)) { +@@ -143,8 +148,9 @@ struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, + /* + * al_find_le + * +- * finds the first 'le' in the list which matches type, name and vcn +- * Returns NULL if not found ++ * Find the first le in the list which matches type, name and VCN. ++ * ++ * Return: NULL if not found. + */ + struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni, + struct ATTR_LIST_ENTRY *le, +@@ -159,8 +165,9 @@ struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni, + /* + * al_find_ex + * +- * finds the first 'le' in the list which matches type, name and vcn +- * Returns NULL if not found ++ * Find the first le in the list which matches type, name and VCN. ++ * ++ * Return: NULL if not found. + */ + struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, + struct ATTR_LIST_ENTRY *le, +@@ -174,7 +181,7 @@ struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, + u64 le_vcn; + int diff = le32_to_cpu(le->type) - type_in; + +- /* List entries are sorted by type, name and vcn */ ++ /* List entries are sorted by type, name and VCN. */ + if (diff < 0) + continue; + +@@ -187,7 +194,7 @@ struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, + le_vcn = le64_to_cpu(le->vcn); + if (!le_vcn) { + /* +- * compare entry names only for entry with vcn == 0 ++ * Compare entry names only for entry with vcn == 0. + */ + diff = ntfs_cmp_names(le_name(le), name_len, name, + name_len, ni->mi.sbi->upcase, +@@ -217,7 +224,7 @@ struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, + /* + * al_find_le_to_insert + * +- * finds the first list entry which matches type, name and vcn ++ * Find the first list entry which matches type, name and VCN. + */ + static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, + enum ATTR_TYPE type, +@@ -227,7 +234,7 @@ static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, + struct ATTR_LIST_ENTRY *le = NULL, *prev; + u32 type_in = le32_to_cpu(type); + +- /* List entries are sorted by type, name, vcn */ ++ /* List entries are sorted by type, name and VCN. */ + while ((le = al_enumerate(ni, prev = le))) { + int diff = le32_to_cpu(le->type) - type_in; + +@@ -239,7 +246,7 @@ static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, + + if (!le->vcn) { + /* +- * compare entry names only for entry with vcn == 0 ++ * Compare entry names only for entry with vcn == 0. + */ + diff = ntfs_cmp_names(le_name(le), le->name_len, name, + name_len, ni->mi.sbi->upcase, +@@ -261,7 +268,7 @@ static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, + /* + * al_add_le + * +- * adds an "attribute list entry" to the list. ++ * Add an "attribute list entry" to the list. + */ + int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref, +@@ -335,9 +342,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + } + + /* +- * al_remove_le +- * +- * removes 'le' from attribute list ++ * al_remove_le - Remove @le from attribute list. + */ + bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) + { +@@ -361,9 +366,7 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) + } + + /* +- * al_delete_le +- * +- * deletes from the list the first 'le' which matches its parameters. ++ * al_delete_le - Delete first le from the list which matches its parameters. + */ + bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, + const __le16 *name, size_t name_len, +@@ -374,7 +377,7 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, + size_t off; + typeof(ni->attr_list) *al = &ni->attr_list; + +- /* Scan forward to the first 'le' that matches the input */ ++ /* Scan forward to the first le that matches the input. */ + le = al_find_ex(ni, NULL, type, name, name_len, &vcn); + if (!le) + return false; +@@ -405,9 +408,9 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, + goto next; + } + +- /* Save on stack the size of 'le' */ ++ /* Save on stack the size of 'le'. */ + size = le16_to_cpu(le->size); +- /* Delete 'le'. */ ++ /* Delete the le. */ + memmove(le, Add2Ptr(le, size), al->size - (off + size)); + + al->size -= size; +@@ -416,9 +419,6 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, + return true; + } + +-/* +- * al_update +- */ + int al_update(struct ntfs_inode *ni) + { + int err; +@@ -429,8 +429,8 @@ int al_update(struct ntfs_inode *ni) + return 0; + + /* +- * attribute list increased on demand in al_add_le +- * attribute list decreased here ++ * Attribute list increased on demand in al_add_le. ++ * Attribute list decreased here. + */ + err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL, + false, &attr); +diff --git a/src/bitfunc.c b/src/bitfunc.c +index 2de5faef2721d5bac9caf7ff81adec167ae21dea..ce304d40b5e1642cc84d48143e2aa961ac3ea735 100644 +--- a/src/bitfunc.c ++++ b/src/bitfunc.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * + */ ++ + #include + #include + #include +@@ -32,7 +33,7 @@ static const u8 zero_mask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, + /* + * are_bits_clear + * +- * Returns true if all bits [bit, bit+nbits) are zeros "0" ++ * Return: True if all bits [bit, bit+nbits) are zeros "0". + */ + bool are_bits_clear(const ulong *lmap, size_t bit, size_t nbits) + { +@@ -74,14 +75,13 @@ bool are_bits_clear(const ulong *lmap, size_t bit, size_t nbits) + if (pos && (*map & fill_mask[pos])) + return false; + +- // All bits are zero + return true; + } + + /* + * are_bits_set + * +- * Returns true if all bits [bit, bit+nbits) are ones "1" ++ * Return: True if all bits [bit, bit+nbits) are ones "1". + */ + bool are_bits_set(const ulong *lmap, size_t bit, size_t nbits) + { +@@ -130,6 +130,5 @@ bool are_bits_set(const ulong *lmap, size_t bit, size_t nbits) + return false; + } + +- // All bits are ones + return true; + } +diff --git a/src/bitmap.c b/src/bitmap.c +index 2de05062c78b0aaa5799329aeb0065f0630f5f54..06ae38adb8ad5601ca134cad3b76871df5d7d3e8 100644 +--- a/src/bitmap.c ++++ b/src/bitmap.c +@@ -6,7 +6,7 @@ + * This code builds two trees of free clusters extents. + * Trees are sorted by start of extent and by length of extent. + * NTFS_MAX_WND_EXTENTS defines the maximum number of elements in trees. +- * In extreme case code reads on-disk bitmap to find free clusters ++ * In extreme case code reads on-disk bitmap to find free clusters. + * + */ + +@@ -29,12 +29,10 @@ struct rb_node_key { + size_t key; + }; + +-/* +- * Tree is sorted by start (key) +- */ ++/* Tree is sorted by start (key). */ + struct e_node { +- struct rb_node_key start; /* Tree sorted by start */ +- struct rb_node_key count; /* Tree sorted by len*/ ++ struct rb_node_key start; /* Tree sorted by start. */ ++ struct rb_node_key count; /* Tree sorted by len. */ + }; + + static int wnd_rescan(struct wnd_bitmap *wnd); +@@ -62,9 +60,12 @@ static inline u32 wnd_bits(const struct wnd_bitmap *wnd, size_t i) + } + + /* +- * b_pos + b_len - biggest fragment +- * Scan range [wpos wbits) window 'buf' +- * Returns -1 if not found ++ * wnd_scan ++ * ++ * b_pos + b_len - biggest fragment. ++ * Scan range [wpos wbits) window @buf. ++ * ++ * Return: -1 if not found. + */ + static size_t wnd_scan(const ulong *buf, size_t wbit, u32 wpos, u32 wend, + size_t to_alloc, size_t *prev_tail, size_t *b_pos, +@@ -96,7 +97,7 @@ static size_t wnd_scan(const ulong *buf, size_t wbit, u32 wpos, u32 wend, + } + + /* +- * Now we have a fragment [wpos, wend) staring with 0 ++ * Now we have a fragment [wpos, wend) staring with 0. + */ + end = wpos + to_alloc - *prev_tail; + free_bits = find_next_bit(buf, min(end, wend), wpos); +@@ -125,9 +126,7 @@ static size_t wnd_scan(const ulong *buf, size_t wbit, u32 wpos, u32 wend, + } + + /* +- * wnd_close +- * +- * Frees all resources ++ * wnd_close - Frees all resources. + */ + void wnd_close(struct wnd_bitmap *wnd) + { +@@ -170,9 +169,7 @@ static struct rb_node *rb_lookup(struct rb_root *root, size_t v) + } + + /* +- * rb_insert_count +- * +- * Helper function to insert special kind of 'count' tree ++ * rb_insert_count - Helper function to insert special kind of 'count' tree. + */ + static inline bool rb_insert_count(struct rb_root *root, struct e_node *e) + { +@@ -205,9 +202,7 @@ static inline bool rb_insert_count(struct rb_root *root, struct e_node *e) + } + + /* +- * inline bool rb_insert_start +- * +- * Helper function to insert special kind of 'start' tree ++ * rb_insert_start - Helper function to insert special kind of 'count' tree. + */ + static inline bool rb_insert_start(struct rb_root *root, struct e_node *e) + { +@@ -237,10 +232,8 @@ static inline bool rb_insert_start(struct rb_root *root, struct e_node *e) + } + + /* +- * wnd_add_free_ext +- * +- * adds a new extent of free space +- * build = 1 when building tree ++ * wnd_add_free_ext - Adds a new extent of free space. ++ * @build: 1 when building tree. + */ + static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + bool build) +@@ -250,14 +243,14 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + struct rb_node *n; + + if (build) { +- /* Use extent_min to filter too short extents */ ++ /* Use extent_min to filter too short extents. */ + if (wnd->count >= NTFS_MAX_WND_EXTENTS && + len <= wnd->extent_min) { + wnd->uptodated = -1; + return; + } + } else { +- /* Try to find extent before 'bit' */ ++ /* Try to find extent before 'bit'. */ + n = rb_lookup(&wnd->start_tree, bit); + + if (!n) { +@@ -266,7 +259,7 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + e = rb_entry(n, struct e_node, start.node); + n = rb_next(n); + if (e->start.key + e->count.key == bit) { +- /* Remove left */ ++ /* Remove left. */ + bit = e->start.key; + len += e->count.key; + rb_erase(&e->start.node, &wnd->start_tree); +@@ -284,7 +277,7 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + if (e->start.key > end_in) + break; + +- /* Remove right */ ++ /* Remove right. */ + n = rb_next(n); + len += next_end - end_in; + end_in = next_end; +@@ -299,7 +292,7 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + } + + if (wnd->uptodated != 1) { +- /* Check bits before 'bit' */ ++ /* Check bits before 'bit'. */ + ib = wnd->zone_bit == wnd->zone_end || + bit < wnd->zone_end + ? 0 +@@ -310,7 +303,7 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + len += 1; + } + +- /* Check bits after 'end_in' */ ++ /* Check bits after 'end_in'. */ + ib = wnd->zone_bit == wnd->zone_end || + end_in > wnd->zone_bit + ? wnd->nbits +@@ -322,29 +315,29 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len, + } + } + } +- /* Insert new fragment */ ++ /* Insert new fragment. */ + if (wnd->count >= NTFS_MAX_WND_EXTENTS) { + if (e0) + kmem_cache_free(ntfs_enode_cachep, e0); + + wnd->uptodated = -1; + +- /* Compare with smallest fragment */ ++ /* Compare with smallest fragment. */ + n = rb_last(&wnd->count_tree); + e = rb_entry(n, struct e_node, count.node); + if (len <= e->count.key) +- goto out; /* Do not insert small fragments */ ++ goto out; /* Do not insert small fragments. */ + + if (build) { + struct e_node *e2; + + n = rb_prev(n); + e2 = rb_entry(n, struct e_node, count.node); +- /* smallest fragment will be 'e2->count.key' */ ++ /* Smallest fragment will be 'e2->count.key'. */ + wnd->extent_min = e2->count.key; + } + +- /* Replace smallest fragment by new one */ ++ /* Replace smallest fragment by new one. */ + rb_erase(&e->start.node, &wnd->start_tree); + rb_erase(&e->count.node, &wnd->count_tree); + wnd->count -= 1; +@@ -371,9 +364,7 @@ out:; + } + + /* +- * wnd_remove_free_ext +- * +- * removes a run from the cached free space ++ * wnd_remove_free_ext - Remove a run from the cached free space. + */ + static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) + { +@@ -382,7 +373,7 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) + size_t end_in = bit + len; + size_t end3, end, new_key, new_len, max_new_len; + +- /* Try to find extent before 'bit' */ ++ /* Try to find extent before 'bit'. */ + n = rb_lookup(&wnd->start_tree, bit); + + if (!n) +@@ -394,11 +385,11 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) + new_key = new_len = 0; + len = e->count.key; + +- /* Range [bit,end_in) must be inside 'e' or outside 'e' and 'n' */ ++ /* Range [bit,end_in) must be inside 'e' or outside 'e' and 'n'. */ + if (e->start.key > bit) + ; + else if (end_in <= end) { +- /* Range [bit,end_in) inside 'e' */ ++ /* Range [bit,end_in) inside 'e'. */ + new_key = end_in; + new_len = end - end_in; + len = bit - e->start.key; +@@ -478,13 +469,13 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) + if (wnd->count >= NTFS_MAX_WND_EXTENTS) { + wnd->uptodated = -1; + +- /* Get minimal extent */ ++ /* Get minimal extent. */ + e = rb_entry(rb_last(&wnd->count_tree), struct e_node, + count.node); + if (e->count.key > new_len) + goto out; + +- /* Replace minimum */ ++ /* Replace minimum. */ + rb_erase(&e->start.node, &wnd->start_tree); + rb_erase(&e->count.node, &wnd->count_tree); + wnd->count -= 1; +@@ -508,9 +499,7 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) + } + + /* +- * wnd_rescan +- * +- * Scan all bitmap. used while initialization. ++ * wnd_rescan - Scan all bitmap. Used while initialization. + */ + static int wnd_rescan(struct wnd_bitmap *wnd) + { +@@ -541,7 +530,7 @@ static int wnd_rescan(struct wnd_bitmap *wnd) + + if (wnd->inited) { + if (!wnd->free_bits[iw]) { +- /* all ones */ ++ /* All ones. */ + if (prev_tail) { + wnd_add_free_ext(wnd, + vbo * 8 - prev_tail, +@@ -551,7 +540,7 @@ static int wnd_rescan(struct wnd_bitmap *wnd) + goto next_wnd; + } + if (wbits == wnd->free_bits[iw]) { +- /* all zeroes */ ++ /* All zeroes. */ + prev_tail += wbits; + wnd->total_zeroes += wbits; + goto next_wnd; +@@ -604,14 +593,14 @@ static int wnd_rescan(struct wnd_bitmap *wnd) + wpos = used; + + if (wpos >= wbits) { +- /* No free blocks */ ++ /* No free blocks. */ + prev_tail = 0; + break; + } + + frb = find_next_bit(buf, wbits, wpos); + if (frb >= wbits) { +- /* keep last free block */ ++ /* Keep last free block. */ + prev_tail += frb - wpos; + break; + } +@@ -619,9 +608,9 @@ static int wnd_rescan(struct wnd_bitmap *wnd) + wnd_add_free_ext(wnd, wbit + wpos - prev_tail, + frb + prev_tail - wpos, true); + +- /* Skip free block and first '1' */ ++ /* Skip free block and first '1'. */ + wpos = frb + 1; +- /* Reset previous tail */ ++ /* Reset previous tail. */ + prev_tail = 0; + } while (wpos < wbits); + +@@ -638,15 +627,15 @@ static int wnd_rescan(struct wnd_bitmap *wnd) + } + } + +- /* Add last block */ ++ /* Add last block. */ + if (prev_tail) + wnd_add_free_ext(wnd, wnd->nbits - prev_tail, prev_tail, true); + + /* +- * Before init cycle wnd->uptodated was 0 ++ * Before init cycle wnd->uptodated was 0. + * If any errors or limits occurs while initialization then +- * wnd->uptodated will be -1 +- * If 'uptodated' is still 0 then Tree is really updated ++ * wnd->uptodated will be -1. ++ * If 'uptodated' is still 0 then Tree is really updated. + */ + if (!wnd->uptodated) + wnd->uptodated = 1; +@@ -662,9 +651,6 @@ static int wnd_rescan(struct wnd_bitmap *wnd) + return err; + } + +-/* +- * wnd_init +- */ + int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits) + { + int err; +@@ -697,9 +683,7 @@ int wnd_init(struct wnd_bitmap *wnd, struct super_block *sb, size_t nbits) + } + + /* +- * wnd_map +- * +- * call sb_bread for requested window ++ * wnd_map - Call sb_bread for requested window. + */ + static struct buffer_head *wnd_map(struct wnd_bitmap *wnd, size_t iw) + { +@@ -728,9 +712,7 @@ static struct buffer_head *wnd_map(struct wnd_bitmap *wnd, size_t iw) + } + + /* +- * wnd_set_free +- * +- * Marks the bits range from bit to bit + bits as free ++ * wnd_set_free - Mark the bits range from bit to bit + bits as free. + */ + int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) + { +@@ -783,9 +765,7 @@ int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) + } + + /* +- * wnd_set_used +- * +- * Marks the bits range from bit to bit + bits as used ++ * wnd_set_used - Mark the bits range from bit to bit + bits as used. + */ + int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) + { +@@ -839,7 +819,7 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) + /* + * wnd_is_free_hlp + * +- * Returns true if all clusters [bit, bit+bits) are free (bitmap only) ++ * Return: True if all clusters [bit, bit+bits) are free (bitmap only). + */ + static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits) + { +@@ -882,7 +862,7 @@ static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits) + /* + * wnd_is_free + * +- * Returns true if all clusters [bit, bit+bits) are free ++ * Return: True if all clusters [bit, bit+bits) are free. + */ + bool wnd_is_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) + { +@@ -914,7 +894,7 @@ bool wnd_is_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) + /* + * wnd_is_used + * +- * Returns true if all clusters [bit, bit+bits) are used ++ * Return: True if all clusters [bit, bit+bits) are used. + */ + bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) + { +@@ -973,11 +953,11 @@ bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) + } + + /* +- * wnd_find ++ * wnd_find - Look for free space. ++ * + * - flags - BITMAP_FIND_XXX flags + * +- * looks for free space +- * Returns 0 if not found ++ * Return: 0 if not found. + */ + size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + size_t flags, size_t *allocated) +@@ -994,7 +974,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + bool fbits_valid; + struct buffer_head *bh; + +- /* fast checking for available free space */ ++ /* Fast checking for available free space. */ + if (flags & BITMAP_FIND_FULL) { + size_t zeroes = wnd_zeroes(wnd); + +@@ -1020,7 +1000,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + + if (RB_EMPTY_ROOT(&wnd->start_tree)) { + if (wnd->uptodated == 1) { +- /* extents tree is updated -> no free space */ ++ /* Extents tree is updated -> No free space. */ + goto no_space; + } + goto scan_bitmap; +@@ -1030,7 +1010,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + if (!hint) + goto allocate_biggest; + +- /* Use hint: enumerate extents by start >= hint */ ++ /* Use hint: Enumerate extents by start >= hint. */ + pr = NULL; + cr = wnd->start_tree.rb_node; + +@@ -1059,7 +1039,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + goto allocate_biggest; + + if (e->start.key + e->count.key > hint) { +- /* We have found extension with 'hint' inside */ ++ /* We have found extension with 'hint' inside. */ + size_t len = e->start.key + e->count.key - hint; + + if (len >= to_alloc && hint + to_alloc <= max_alloc) { +@@ -1080,7 +1060,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + } + + allocate_biggest: +- /* Allocate from biggest free extent */ ++ /* Allocate from biggest free extent. */ + e = rb_entry(rb_first(&wnd->count_tree), struct e_node, count.node); + if (e->count.key != wnd->extent_max) + wnd->extent_max = e->count.key; +@@ -1090,14 +1070,14 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + ; + } else if (flags & BITMAP_FIND_FULL) { + if (e->count.key < to_alloc0) { +- /* Biggest free block is less then requested */ ++ /* Biggest free block is less then requested. */ + goto no_space; + } + to_alloc = e->count.key; + } else if (-1 != wnd->uptodated) { + to_alloc = e->count.key; + } else { +- /* Check if we can use more bits */ ++ /* Check if we can use more bits. */ + size_t op, max_check; + struct rb_root start_tree; + +@@ -1118,7 +1098,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + to_alloc = op - e->start.key; + } + +- /* Prepare to return */ ++ /* Prepare to return. */ + fnd = e->start.key; + if (e->start.key + to_alloc > max_alloc) + to_alloc = max_alloc - e->start.key; +@@ -1126,7 +1106,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + } + + if (wnd->uptodated == 1) { +- /* extents tree is updated -> no free space */ ++ /* Extents tree is updated -> no free space. */ + goto no_space; + } + +@@ -1140,7 +1120,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + /* At most two ranges [hint, max_alloc) + [0, hint) */ + Again: + +- /* TODO: optimize request for case nbits > wbits */ ++ /* TODO: Optimize request for case nbits > wbits. */ + iw = hint >> log2_bits; + wbits = sb->s_blocksize * 8; + wpos = hint & (wbits - 1); +@@ -1155,7 +1135,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + nwnd = likely(t > max_alloc) ? (t >> log2_bits) : wnd->nwnd; + } + +- /* Enumerate all windows */ ++ /* Enumerate all windows. */ + for (; iw < nwnd; iw++) { + wbit = iw << log2_bits; + +@@ -1165,7 +1145,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + b_len = prev_tail; + } + +- /* Skip full used window */ ++ /* Skip full used window. */ + prev_tail = 0; + wpos = 0; + continue; +@@ -1189,25 +1169,25 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + zbit = max(wnd->zone_bit, wbit); + zend = min(wnd->zone_end, ebit); + +- /* Here we have a window [wbit, ebit) and zone [zbit, zend) */ ++ /* Here we have a window [wbit, ebit) and zone [zbit, zend). */ + if (zend <= zbit) { +- /* Zone does not overlap window */ ++ /* Zone does not overlap window. */ + } else { + wzbit = zbit - wbit; + wzend = zend - wbit; + +- /* Zone overlaps window */ ++ /* Zone overlaps window. */ + if (wnd->free_bits[iw] == wzend - wzbit) { + prev_tail = 0; + wpos = 0; + continue; + } + +- /* Scan two ranges window: [wbit, zbit) and [zend, ebit) */ ++ /* Scan two ranges window: [wbit, zbit) and [zend, ebit). */ + bh = wnd_map(wnd, iw); + + if (IS_ERR(bh)) { +- /* TODO: error */ ++ /* TODO: Error */ + prev_tail = 0; + wpos = 0; + continue; +@@ -1215,9 +1195,9 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + + buf = (ulong *)bh->b_data; + +- /* Scan range [wbit, zbit) */ ++ /* Scan range [wbit, zbit). */ + if (wpos < wzbit) { +- /* Scan range [wpos, zbit) */ ++ /* Scan range [wpos, zbit). */ + fnd = wnd_scan(buf, wbit, wpos, wzbit, + to_alloc, &prev_tail, + &b_pos, &b_len); +@@ -1229,7 +1209,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + + prev_tail = 0; + +- /* Scan range [zend, ebit) */ ++ /* Scan range [zend, ebit). */ + if (wzend < wbits) { + fnd = wnd_scan(buf, wbit, + max(wzend, wpos), wbits, +@@ -1247,24 +1227,24 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + } + } + +- /* Current window does not overlap zone */ ++ /* Current window does not overlap zone. */ + if (!wpos && fbits_valid && wnd->free_bits[iw] == wbits) { +- /* window is empty */ ++ /* Window is empty. */ + if (prev_tail + wbits >= to_alloc) { + fnd = wbit + wpos - prev_tail; + goto found; + } + +- /* Increase 'prev_tail' and process next window */ ++ /* Increase 'prev_tail' and process next window. */ + prev_tail += wbits; + wpos = 0; + continue; + } + +- /* read window */ ++ /* Read window */ + bh = wnd_map(wnd, iw); + if (IS_ERR(bh)) { +- // TODO: error ++ // TODO: Error. + prev_tail = 0; + wpos = 0; + continue; +@@ -1272,7 +1252,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + + buf = (ulong *)bh->b_data; + +- /* Scan range [wpos, eBits) */ ++ /* Scan range [wpos, eBits). */ + fnd = wnd_scan(buf, wbit, wpos, wbits, to_alloc, &prev_tail, + &b_pos, &b_len); + put_bh(bh); +@@ -1281,15 +1261,15 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + } + + if (b_len < prev_tail) { +- /* The last fragment */ ++ /* The last fragment. */ + b_len = prev_tail; + b_pos = max_alloc - prev_tail; + } + + if (hint) { + /* +- * We have scanned range [hint max_alloc) +- * Prepare to scan range [0 hint + to_alloc) ++ * We have scanned range [hint max_alloc). ++ * Prepare to scan range [0 hint + to_alloc). + */ + size_t nextmax = hint + to_alloc; + +@@ -1312,7 +1292,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + + found: + if (flags & BITMAP_FIND_MARK_AS_USED) { +- /* TODO optimize remove extent (pass 'e'?) */ ++ /* TODO: Optimize remove extent (pass 'e'?). */ + if (wnd_set_used(wnd, fnd, to_alloc)) + goto no_space; + } else if (wnd->extent_max != MINUS_ONE_T && +@@ -1328,9 +1308,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + } + + /* +- * wnd_extend +- * +- * Extend bitmap ($MFT bitmap) ++ * wnd_extend - Extend bitmap ($MFT bitmap). + */ + int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + { +@@ -1347,7 +1325,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + if (new_bits <= old_bits) + return -EINVAL; + +- /* align to 8 byte boundary */ ++ /* Align to 8 byte boundary. */ + new_wnd = bytes_to_block(sb, bitmap_size(new_bits)); + new_last = new_bits & (wbits - 1); + if (!new_last) +@@ -1367,7 +1345,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + wnd->free_bits = new_free; + } + +- /* Zero bits [old_bits,new_bits) */ ++ /* Zero bits [old_bits,new_bits). */ + bits = new_bits - old_bits; + b0 = old_bits & (wbits - 1); + +@@ -1403,7 +1381,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); +- /*err = sync_dirty_buffer(bh);*/ ++ /* err = sync_dirty_buffer(bh); */ + + b0 = 0; + bits -= op; +@@ -1418,9 +1396,6 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits) + return 0; + } + +-/* +- * wnd_zone_set +- */ + void wnd_zone_set(struct wnd_bitmap *wnd, size_t lcn, size_t len) + { + size_t zlen; +@@ -1502,7 +1477,7 @@ int ntfs_trim_fs(struct ntfs_sb_info *sbi, struct fstrim_range *range) + put_bh(bh); + } + +- /* Process the last fragment */ ++ /* Process the last fragment. */ + if (len >= minlen) { + err = ntfs_discard(sbi, lcn, len); + if (err) +diff --git a/src/debug.h b/src/debug.h +index c8403e876b2dc522520921c58fa230a4df4e04d1..31120569a87b9b07cf91f8e0b8c7291db059f4f3 100644 +--- a/src/debug.h ++++ b/src/debug.h +@@ -3,7 +3,8 @@ + * + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * +- * useful functions for debugging ++ * Useful functions for debugging. ++ * + */ + + // clang-format off +@@ -33,7 +34,7 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + #endif + + /* +- * Logging macros ( thanks Joe Perches for implementation ) ++ * Logging macros. Thanks Joe Perches for implementation. + */ + + #define ntfs_err(sb, fmt, ...) ntfs_printk(sb, KERN_ERR fmt, ##__VA_ARGS__) +diff --git a/src/dir.c b/src/dir.c +index d36d7fbc2b1dded9e1dea068a3f7ccd2783607ce..93f6d485564e0103e56ad417f184f1ace0926eea 100644 +--- a/src/dir.c ++++ b/src/dir.c +@@ -3,9 +3,10 @@ + * + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * +- * directory handling functions for ntfs-based filesystems ++ * Directory handling functions for NTFS-based filesystems. + * + */ ++ + #include + #include + #include +@@ -16,9 +17,7 @@ + #include "ntfs.h" + #include "ntfs_fs.h" + +-/* +- * Convert little endian utf16 to nls string +- */ ++/* Convert little endian UTF-16 to NLS string. */ + int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + u8 *buf, int buf_len) + { +@@ -30,7 +29,7 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + static_assert(sizeof(wchar_t) == sizeof(__le16)); + + if (!nls) { +- /* utf16 -> utf8 */ ++ /* UTF-16 -> UTF-8 */ + ret = utf16s_to_utf8s((wchar_t *)uni->name, uni->len, + UTF16_LITTLE_ENDIAN, buf, buf_len); + buf[ret] = '\0'; +@@ -89,8 +88,9 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + // clang-format on + + /* +- * modified version of put_utf16 from fs/nls/nls_base.c +- * is sparse warnings free ++ * put_utf16 - Modified version of put_utf16 from fs/nls/nls_base.c ++ * ++ * Function is sparse warnings free. + */ + static inline void put_utf16(wchar_t *s, unsigned int c, + enum utf16_endian endian) +@@ -112,8 +112,10 @@ static inline void put_utf16(wchar_t *s, unsigned int c, + } + + /* +- * modified version of 'utf8s_to_utf16s' allows to +- * detect -ENAMETOOLONG without writing out of expected maximum ++ * _utf8s_to_utf16s ++ * ++ * Modified version of 'utf8s_to_utf16s' allows to ++ * detect -ENAMETOOLONG without writing out of expected maximum. + */ + static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian, + wchar_t *pwcs, int maxout) +@@ -165,17 +167,18 @@ static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian, + } + + /* +- * Convert input string to utf16 +- * +- * name, name_len - input name +- * uni, max_ulen - destination memory +- * endian - endian of target utf16 string ++ * ntfs_nls_to_utf16 - Convert input string to UTF-16. ++ * @name: Input name. ++ * @name_len: Input name length. ++ * @uni: Destination memory. ++ * @max_ulen: Destination memory. ++ * @endian: Endian of target UTF-16 string. + * + * This function is called: +- * - to create ntfs name ++ * - to create NTFS name + * - to create symlink + * +- * returns utf16 string length or error (if negative) ++ * Return: UTF-16 string length or error (if negative). + */ + int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, + struct cpu_str *uni, u32 max_ulen, +@@ -230,7 +233,9 @@ int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, + return ret; + } + +-/* helper function */ ++/* ++ * dir_search_u - Helper function. ++ */ + struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, + struct ntfs_fnd *fnd) + { +@@ -295,7 +300,7 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + if (ino == MFT_REC_ROOT) + return 0; + +- /* Skip meta files ( unless option to show metafiles is set ) */ ++ /* Skip meta files. Unless option to show metafiles is set. */ + if (!sbi->options.showmeta && ntfs_is_meta_file(sbi, ino)) + return 0; + +@@ -316,9 +321,7 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + } + + /* +- * ntfs_read_hdr +- * +- * helper function 'ntfs_readdir' ++ * ntfs_read_hdr - Helper function for ntfs_readdir(). + */ + static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + const struct INDEX_HDR *hdr, u64 vbo, u64 pos, +@@ -342,7 +345,7 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + if (de_is_last(e)) + return 0; + +- /* Skip already enumerated*/ ++ /* Skip already enumerated. */ + if (vbo + off < pos) + continue; + +@@ -359,11 +362,11 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + } + + /* +- * file_operations::iterate_shared ++ * ntfs_readdir - file_operations::iterate_shared + * + * Use non sorted enumeration. + * We have an example of broken volume where sorted enumeration +- * counts each name twice ++ * counts each name twice. + */ + static int ntfs_readdir(struct file *file, struct dir_context *ctx) + { +@@ -382,7 +385,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) + struct indx_node *node = NULL; + u8 index_bits = ni->dir.index_bits; + +- /* name is a buffer of PATH_MAX length */ ++ /* Name is a buffer of PATH_MAX length. */ + static_assert(NTFS_NAME_LEN * 4 < PATH_MAX); + + eod = i_size + sbi->record_size; +@@ -393,16 +396,16 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx) + if (!dir_emit_dots(file, ctx)) + return 0; + +- /* allocate PATH_MAX bytes */ ++ /* Allocate PATH_MAX bytes. */ + name = __getname(); + if (!name) + return -ENOMEM; + + if (!ni->mi_loaded && ni->attr_list.size) { + /* +- * directory inode is locked for read +- * load all subrecords to avoid 'write' access to 'ni' during +- * directory reading ++ * Directory inode is locked for read. ++ * Load all subrecords to avoid 'write' access to 'ni' during ++ * directory reading. + */ + ni_lock(ni); + if (!ni->mi_loaded && ni->attr_list.size) { +diff --git a/src/file.c b/src/file.c +index a959f6197c99e55275e12ffa78b9193f87bef942..26346771d9dc765c6faddc96913bca512e1f768a 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -3,8 +3,10 @@ + * + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * +- * regular file handling primitives for ntfs-based filesystems ++ * Regular file handling primitives for NTFS-based filesystems. ++ * + */ ++ + #include + #include + #include +@@ -62,7 +64,7 @@ static long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) + case FITRIM: + return ntfs_ioctl_fitrim(sbi, arg); + } +- return -ENOTTY; /* Inappropriate ioctl for device */ ++ return -ENOTTY; /* Inappropriate ioctl for device. */ + } + + #ifdef CONFIG_COMPAT +@@ -74,7 +76,7 @@ static long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg) + #endif + + /* +- * inode_operations::getattr ++ * ntfs_getattr - inode_operations::getattr + */ + int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, + struct kstat *stat, u32 request_mask, u32 flags) +@@ -170,7 +172,7 @@ static int ntfs_extend_initialized_size(struct file *file, + + zero_user_segment(page, zerofrom, PAGE_SIZE); + +- /* this function in any case puts page*/ ++ /* This function in any case puts page. */ + err = pagecache_write_end(file, mapping, pos, len, len, page, + fsdata); + if (err < 0) +@@ -195,9 +197,7 @@ static int ntfs_extend_initialized_size(struct file *file, + } + + /* +- * ntfs_zero_range +- * +- * Helper function for punch_hole. ++ * ntfs_zero_range - Helper function for punch_hole. + * It zeroes a range [vbo, vbo_to) + */ + static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) +@@ -356,7 +356,7 @@ void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn, + } + + /* +- * file_operations::mmap ++ * ntfs_file_mmap - file_operations::mmap + */ + static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma) + { +@@ -387,7 +387,7 @@ static int ntfs_file_mmap(struct file *file, struct vm_area_struct *vma) + from + vma->vm_end - vma->vm_start); + + if (is_sparsed(ni)) { +- /* allocate clusters for rw map */ ++ /* Allocate clusters for rw map. */ + struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; + CLST lcn, len; + CLST vcn = from >> sbi->cluster_bits; +@@ -436,7 +436,7 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count, + if (end <= inode->i_size && !extend_init) + return 0; + +- /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ /* Mark rw ntfs as dirty. It will be cleared at umount. */ + ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY); + + if (end > inode->i_size) { +@@ -530,6 +530,8 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size) + } + + /* ++ * ntfs_fallocate ++ * + * Preallocate space for a file. This implements ntfs's fallocate file + * operation, which gets called from sys_fallocate system call. User + * space requests 'len' bytes at 'vbo'. If FALLOC_FL_KEEP_SIZE is set +@@ -547,11 +549,11 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + loff_t i_size; + int err; + +- /* No support for dir */ ++ /* No support for dir. */ + if (!S_ISREG(inode->i_mode)) + return -EOPNOTSUPP; + +- /* Return error if mode is not supported */ ++ /* Return error if mode is not supported. */ + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | + FALLOC_FL_COLLAPSE_RANGE)) { + ntfs_inode_warn(inode, "fallocate(0x%x) is not supported", +@@ -565,7 +567,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + i_size = inode->i_size; + + if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { +- /* should never be here, see ntfs_file_open*/ ++ /* Should never be here, see ntfs_file_open. */ + err = -EOPNOTSUPP; + goto out; + } +@@ -646,7 +648,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + + /* + * Write data that will be shifted to preserve them +- * when discarding page cache below ++ * when discarding page cache below. + */ + err = filemap_write_and_wait_range(inode->i_mapping, end, + LLONG_MAX); +@@ -663,7 +665,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + ni_unlock(ni); + } else { + /* +- * normal file: allocate clusters, do not change 'valid' size ++ * Normal file: Allocate clusters, do not change 'valid' size. + */ + err = ntfs_set_size(inode, max(end, i_size)); + if (err) +@@ -677,10 +679,10 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + bool new; + + /* +- * allocate but not zero new clusters (see below comments) +- * this breaks security (one can read unused on-disk areas) +- * zeroing these clusters may be too long +- * may be we should check here for root rights? ++ * Allocate but do not zero new clusters. (see below comments) ++ * This breaks security: One can read unused on-disk areas. ++ * Zeroing these clusters may be too long. ++ * Maybe we should check here for root rights? + */ + for (; vcn < cend; vcn += clen) { + err = attr_data_get_block(ni, vcn, cend - vcn, +@@ -691,15 +693,15 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + continue; + + /* +- * Unwritten area +- * NTFS is not able to store several unwritten areas +- * Activate 'ntfs_sparse_cluster' to zero new allocated clusters ++ * Unwritten area. ++ * NTFS is not able to store several unwritten areas. ++ * Activate 'ntfs_sparse_cluster' to zero new allocated clusters. + * + * Dangerous in case: + * 1G of sparsed clusters + 1 cluster of data => + * valid_size == 1G + 1 cluster + * fallocate(1G) will zero 1G and this can be very long +- * xfstest 016/086 will fail without 'ntfs_sparse_cluster' ++ * xfstest 016/086 will fail without 'ntfs_sparse_cluster'. + */ + ntfs_sparse_cluster(inode, NULL, vcn, + min(vcn_v - vcn, clen)); +@@ -708,7 +710,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + + if (mode & FALLOC_FL_KEEP_SIZE) { + ni_lock(ni); +- /*true - keep preallocated*/ ++ /* True - Keep preallocated. */ + err = attr_set_size(ni, ATTR_DATA, NULL, 0, + &ni->file.run, i_size, &ni->i_valid, + true, NULL); +@@ -730,7 +732,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + } + + /* +- * inode_operations::setattr ++ * ntfs3_setattr - inode_operations::setattr + */ + int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + struct iattr *attr) +@@ -744,9 +746,9 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + int err; + + if (sbi->options.no_acs_rules) { +- /* "no access rules" - force any changes of time etc. */ ++ /* "No access rules" - Force any changes of time etc. */ + attr->ia_valid |= ATTR_FORCE; +- /* and disable for editing some attributes */ ++ /* and disable for editing some attributes. */ + attr->ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE); + ia_valid = attr->ia_valid; + } +@@ -759,7 +761,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + loff_t oldsize = inode->i_size; + + if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { +- /* should never be here, see ntfs_file_open*/ ++ /* Should never be here, see ntfs_file_open(). */ + err = -EOPNOTSUPP; + goto out; + } +@@ -783,7 +785,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + if (err) + goto out; + +- /* linux 'w' -> windows 'ro' */ ++ /* Linux 'w' -> Windows 'ro'. */ + if (0222 & inode->i_mode) + ni->std_fa &= ~FILE_ATTRIBUTE_READONLY; + else +@@ -834,7 +836,11 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) + return err; + } + +-/* returns array of locked pages */ ++/* ++ * ntfs_get_frame_pages ++ * ++ * Return: Array of locked pages. ++ */ + static int ntfs_get_frame_pages(struct address_space *mapping, pgoff_t index, + struct page **pages, u32 pages_per_frame, + bool *frame_uptodate) +@@ -867,7 +873,9 @@ static int ntfs_get_frame_pages(struct address_space *mapping, pgoff_t index, + return 0; + } + +-/*helper for ntfs_file_write_iter (compressed files)*/ ++/* ++ * ntfs_compress_write - Helper for ntfs_file_write_iter() (compressed files). ++ */ + static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + { + int err; +@@ -913,7 +921,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + if (err) + goto out; + +- /* zero range [valid : pos) */ ++ /* Zero range [valid : pos). */ + while (valid < pos) { + CLST lcn, clen; + +@@ -932,7 +940,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + continue; + } + +- /* Load full frame */ ++ /* Load full frame. */ + err = ntfs_get_frame_pages(mapping, frame_vbo >> PAGE_SHIFT, + pages, pages_per_frame, + &frame_uptodate); +@@ -978,7 +986,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + ni->i_valid = valid = frame_vbo + frame_size; + } + +- /* copy user data [pos : pos + count) */ ++ /* Copy user data [pos : pos + count). */ + while (count) { + size_t copied, bytes; + +@@ -996,7 +1004,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + goto out; + } + +- /* Load full frame */ ++ /* Load full frame. */ + err = ntfs_get_frame_pages(mapping, index, pages, + pages_per_frame, &frame_uptodate); + if (err) +@@ -1025,7 +1033,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + ip = off >> PAGE_SHIFT; + off = offset_in_page(pos); + +- /* copy user data to pages */ ++ /* Copy user data to pages. */ + for (;;) { + size_t cp, tail = PAGE_SIZE - off; + +@@ -1091,7 +1099,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + } + + /* +- * file_operations::write_iter ++ * ntfs_file_write_iter - file_operations::write_iter + */ + static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) + { +@@ -1127,7 +1135,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) + goto out; + + if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { +- /* should never be here, see ntfs_file_open*/ ++ /* Should never be here, see ntfs_file_open() */ + ret = -EOPNOTSUPP; + goto out; + } +@@ -1149,7 +1157,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) + } + + /* +- * file_operations::open ++ * ntfs_file_open - file_operations::open + */ + int ntfs_file_open(struct inode *inode, struct file *file) + { +@@ -1160,7 +1168,7 @@ int ntfs_file_open(struct inode *inode, struct file *file) + return -EOPNOTSUPP; + } + +- /* Decompress "external compressed" file if opened for rw */ ++ /* Decompress "external compressed" file if opened for rw. */ + if ((ni->ni_flags & NI_FLAG_COMPRESSED_MASK) && + (file->f_flags & (O_WRONLY | O_RDWR | O_TRUNC))) { + #ifdef CONFIG_NTFS3_LZX_XPRESS +@@ -1180,7 +1188,7 @@ int ntfs_file_open(struct inode *inode, struct file *file) + } + + /* +- * file_operations::release ++ * ntfs_file_release - file_operations::release + */ + static int ntfs_file_release(struct inode *inode, struct file *file) + { +@@ -1188,7 +1196,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file) + struct ntfs_sb_info *sbi = ni->mi.sbi; + int err = 0; + +- /* if we are the last writer on the inode, drop the block reservation */ ++ /* If we are last writer on the inode, drop the block reservation. */ + if (sbi->options.prealloc && ((file->f_mode & FMODE_WRITE) && + atomic_read(&inode->i_writecount) == 1)) { + ni_lock(ni); +@@ -1203,7 +1211,9 @@ static int ntfs_file_release(struct inode *inode, struct file *file) + return err; + } + +-/* file_operations::fiemap */ ++/* ++ * ntfs_fiemap - file_operations::fiemap ++ */ + int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len) + { +diff --git a/src/frecord.c b/src/frecord.c +index 329bc76dfb09cb5d2a8e0215805d9a738c35bde6..9d374827750b2b39261f5985ec2fbb554ac8f6df 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -47,9 +47,7 @@ static struct mft_inode *ni_ins_mi(struct ntfs_inode *ni, struct rb_root *tree, + } + + /* +- * ni_find_mi +- * +- * finds mft_inode by record number ++ * ni_find_mi - Find mft_inode by record number. + */ + static struct mft_inode *ni_find_mi(struct ntfs_inode *ni, CLST rno) + { +@@ -57,29 +55,24 @@ static struct mft_inode *ni_find_mi(struct ntfs_inode *ni, CLST rno) + } + + /* +- * ni_add_mi +- * +- * adds new mft_inode into ntfs_inode +- */ ++ * ni_add_mi - Add new mft_inode into ntfs_inode. ++*/ + static void ni_add_mi(struct ntfs_inode *ni, struct mft_inode *mi) + { + ni_ins_mi(ni, &ni->mi_tree, mi->rno, &mi->node); + } + + /* +- * ni_remove_mi +- * +- * removes mft_inode from ntfs_inode ++ * ni_remove_mi - Remove mft_inode from ntfs_inode. + */ + void ni_remove_mi(struct ntfs_inode *ni, struct mft_inode *mi) + { + rb_erase(&mi->node, &ni->mi_tree); + } + +-/* +- * ni_std ++/* ni_std + * +- * returns pointer into std_info from primary record ++ * Return: Pointer into std_info from primary record. + */ + struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni) + { +@@ -93,7 +86,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni) + /* + * ni_std5 + * +- * returns pointer into std_info from primary record ++ * Return: Pointer into std_info from primary record. + */ + struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni) + { +@@ -106,9 +99,7 @@ struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni) + } + + /* +- * ni_clear +- * +- * clears resources allocated by ntfs_inode ++ * ni_clear - Clear resources allocated by ntfs_inode. + */ + void ni_clear(struct ntfs_inode *ni) + { +@@ -128,14 +119,14 @@ void ni_clear(struct ntfs_inode *ni) + node = next; + } + +- /* bad inode always has mode == S_IFREG */ ++ /* Bad inode always has mode == S_IFREG. */ + if (ni->ni_flags & NI_FLAG_DIR) + indx_clear(&ni->dir); + else { + run_close(&ni->file.run); + #ifdef CONFIG_NTFS3_LZX_XPRESS + if (ni->file.offs_page) { +- /* on-demand allocated page for offsets */ ++ /* On-demand allocated page for offsets. */ + put_page(ni->file.offs_page); + ni->file.offs_page = NULL; + } +@@ -146,9 +137,7 @@ void ni_clear(struct ntfs_inode *ni) + } + + /* +- * ni_load_mi_ex +- * +- * finds mft_inode by record number. ++ * ni_load_mi_ex - Find mft_inode by record number. + */ + int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + { +@@ -172,9 +161,7 @@ int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + } + + /* +- * ni_load_mi +- * +- * load mft_inode corresponded list_entry ++ * ni_load_mi - Load mft_inode corresponded list_entry. + */ + int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + struct mft_inode **mi) +@@ -197,7 +184,7 @@ int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + /* + * ni_find_attr + * +- * returns attribute and record this attribute belongs to ++ * Return: Attribute and record this attribute belongs to. + */ + struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, + struct ATTR_LIST_ENTRY **le_o, enum ATTR_TYPE type, +@@ -214,11 +201,11 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, + if (mi) + *mi = &ni->mi; + +- /* Look for required attribute in primary record */ ++ /* Look for required attribute in primary record. */ + return mi_find_attr(&ni->mi, attr, type, name, name_len, NULL); + } + +- /* first look for list entry of required type */ ++ /* First look for list entry of required type. */ + le = al_find_ex(ni, le_o ? *le_o : NULL, type, name, name_len, vcn); + if (!le) + return NULL; +@@ -226,11 +213,11 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, + if (le_o) + *le_o = le; + +- /* Load record that contains this attribute */ ++ /* Load record that contains this attribute. */ + if (ni_load_mi(ni, le, &m)) + return NULL; + +- /* Look for required attribute */ ++ /* Look for required attribute. */ + attr = mi_find_attr(m, NULL, type, name, name_len, &le->id); + + if (!attr) +@@ -257,9 +244,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, + } + + /* +- * ni_enum_attr_ex +- * +- * enumerates attributes in ntfs_inode ++ * ni_enum_attr_ex - Enumerates attributes in ntfs_inode. + */ + struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, + struct ATTR_LIST_ENTRY **le, +@@ -273,30 +258,28 @@ struct ATTRIB *ni_enum_attr_ex(struct ntfs_inode *ni, struct ATTRIB *attr, + *le = NULL; + if (mi) + *mi = &ni->mi; +- /* Enum attributes in primary record */ ++ /* Enum attributes in primary record. */ + return mi_enum_attr(&ni->mi, attr); + } + +- /* get next list entry */ ++ /* Get next list entry. */ + le2 = *le = al_enumerate(ni, attr ? *le : NULL); + if (!le2) + return NULL; + +- /* Load record that contains the required attribute */ ++ /* Load record that contains the required attribute. */ + if (ni_load_mi(ni, le2, &mi2)) + return NULL; + + if (mi) + *mi = mi2; + +- /* Find attribute in loaded record */ ++ /* Find attribute in loaded record. */ + return rec_find_attr_le(mi2, le2); + } + + /* +- * ni_load_attr +- * +- * loads attribute that contains given vcn ++ * ni_load_attr - Load attribute that contains given VCN. + */ + struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, CLST vcn, +@@ -318,9 +301,9 @@ struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + return NULL; + + /* +- * Unfortunately ATTR_LIST_ENTRY contains only start vcn ++ * Unfortunately ATTR_LIST_ENTRY contains only start VCN. + * So to find the ATTRIB segment that contains 'vcn' we should +- * enumerate some entries ++ * enumerate some entries. + */ + if (vcn) { + for (;; le = next) { +@@ -351,9 +334,7 @@ struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + /* +- * ni_load_all_mi +- * +- * loads all subrecords ++ * ni_load_all_mi - Load all subrecords. + */ + int ni_load_all_mi(struct ntfs_inode *ni) + { +@@ -380,9 +361,7 @@ int ni_load_all_mi(struct ntfs_inode *ni) + } + + /* +- * ni_add_subrecord +- * +- * allocate + format + attach a new subrecord ++ * ni_add_subrecord - Allocate + format + attach a new subrecord. + */ + bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + { +@@ -405,10 +384,8 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + } + + /* +- * ni_remove_attr +- * +- * removes all attributes for the given type/name/id +- */ ++ * ni_remove_attr - Remove all attributes for the given type/name/id. ++*/ + int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, size_t name_len, bool base_only, + const __le16 *id) +@@ -473,10 +450,9 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + /* +- * ni_ins_new_attr ++ * ni_ins_new_attr - Insert the attribute into record. + * +- * inserts the attribute into record +- * Returns not full constructed attribute or NULL if not possible to create ++ * Return: Not full constructed attribute or NULL if not possible to create. + */ + static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, + struct mft_inode *mi, +@@ -496,7 +472,7 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, + err = al_add_le(ni, type, name, name_len, svcn, cpu_to_le16(-1), + &ref, &le); + if (err) { +- /* no memory or no space */ ++ /* No memory or no space. */ + return NULL; + } + le_added = true; +@@ -505,7 +481,7 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, + * al_add_le -> attr_set_size (list) -> ni_expand_list + * which moves some attributes out of primary record + * this means that name may point into moved memory +- * reinit 'name' from le ++ * reinit 'name' from le. + */ + name = le->name; + } +@@ -518,14 +494,14 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, + } + + if (type == ATTR_LIST) { +- /*attr list is not in list entry array*/ ++ /* Attr list is not in list entry array. */ + goto out; + } + + if (!le) + goto out; + +- /* Update ATTRIB Id and record reference */ ++ /* Update ATTRIB Id and record reference. */ + le->id = attr->id; + ni->attr_list.dirty = true; + le->ref = ref; +@@ -535,9 +511,11 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, + } + + /* +- * random write access to sparsed or compressed file may result to ++ * ni_repack ++ * ++ * Random write access to sparsed or compressed file may result to + * not optimized packed runs. +- * Here it is the place to optimize it ++ * Here is the place to optimize it. + */ + static int ni_repack(struct ntfs_inode *ni) + { +@@ -581,11 +559,11 @@ static int ni_repack(struct ntfs_inode *ni) + } + + if (!mi_p) { +- /* do not try if too little free space */ ++ /* Do not try if not enogh free space. */ + if (le32_to_cpu(mi->mrec->used) + 8 >= rs) + continue; + +- /* do not try if last attribute segment */ ++ /* Do not try if last attribute segment. */ + if (evcn + 1 == alloc) + continue; + run_close(&run); +@@ -609,8 +587,8 @@ static int ni_repack(struct ntfs_inode *ni) + } + + /* +- * run contains data from two records: mi_p and mi +- * try to pack in one ++ * Run contains data from two records: mi_p and mi ++ * Try to pack in one. + */ + err = mi_pack_runs(mi_p, attr_p, &run, evcn + 1 - svcn_p); + if (err) +@@ -619,7 +597,7 @@ static int ni_repack(struct ntfs_inode *ni) + next_svcn = le64_to_cpu(attr_p->nres.evcn) + 1; + + if (next_svcn >= evcn + 1) { +- /* we can remove this attribute segment */ ++ /* We can remove this attribute segment. */ + al_remove_le(ni, le); + mi_remove_attr(mi, attr); + le = le_p; +@@ -650,7 +628,7 @@ static int ni_repack(struct ntfs_inode *ni) + ntfs_inode_warn(&ni->vfs_inode, "repack problem"); + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + +- /* Pack loaded but not packed runs */ ++ /* Pack loaded but not packed runs. */ + if (mi_p) + mi_pack_runs(mi_p, attr_p, &run, evcn_p + 1 - svcn_p); + } +@@ -663,7 +641,7 @@ static int ni_repack(struct ntfs_inode *ni) + * ni_try_remove_attr_list + * + * Can we remove attribute list? +- * Check the case when primary record contains enough space for all attributes ++ * Check the case when primary record contains enough space for all attributes. + */ + static int ni_try_remove_attr_list(struct ntfs_inode *ni) + { +@@ -689,7 +667,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + + asize = le32_to_cpu(attr_list->size); + +- /* free space in primary record without attribute list */ ++ /* Free space in primary record without attribute list. */ + free = sbi->record_size - le32_to_cpu(ni->mi.mrec->used) + asize; + mi_get_ref(&ni->mi, &ref); + +@@ -717,7 +695,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + free -= asize; + } + +- /* Is seems that attribute list can be removed from primary record */ ++ /* Is seems that attribute list can be removed from primary record. */ + mi_remove_attr(&ni->mi, attr_list); + + /* +@@ -735,17 +713,17 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + le->name_len, &le->id); + asize = le32_to_cpu(attr->size); + +- /* insert into primary record */ ++ /* Insert into primary record. */ + attr_ins = mi_insert_attr(&ni->mi, le->type, le_name(le), + le->name_len, asize, + le16_to_cpu(attr->name_off)); + id = attr_ins->id; + +- /* copy all except id */ ++ /* Copy all except id. */ + memcpy(attr_ins, attr, asize); + attr_ins->id = id; + +- /* remove from original record */ ++ /* Remove from original record. */ + mi_remove_attr(mi, attr); + } + +@@ -760,10 +738,8 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + } + + /* +- * ni_create_attr_list +- * +- * generates an attribute list for this primary record +- */ ++ * ni_create_attr_list - Generates an attribute list for this primary record. ++*/ + int ni_create_attr_list(struct ntfs_inode *ni) + { + struct ntfs_sb_info *sbi = ni->mi.sbi; +@@ -784,8 +760,8 @@ int ni_create_attr_list(struct ntfs_inode *ni) + rs = sbi->record_size; + + /* +- * Skip estimating exact memory requirement +- * Looks like one record_size is always enough ++ * Skip estimating exact memory requirement. ++ * Looks like one record_size is always enough. + */ + le = kmalloc(al_aligned(rs), GFP_NOFS); + if (!le) { +@@ -844,12 +820,12 @@ int ni_create_attr_list(struct ntfs_inode *ni) + } + } + +- /* Allocate child mft. */ ++ /* Allocate child MFT. */ + err = ntfs_look_free_mft(sbi, &rno, is_mft, ni, &mi); + if (err) + goto out1; + +- /* Call 'mi_remove_attr' in reverse order to keep pointers 'arr_move' valid */ ++ /* Call mi_remove_attr() in reverse order to keep pointers 'arr_move' valid. */ + while (to_free > 0) { + struct ATTRIB *b = arr_move[--nb]; + u32 asize = le32_to_cpu(b->size); +@@ -862,7 +838,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) + mi_get_ref(mi, &le_b[nb]->ref); + le_b[nb]->id = attr->id; + +- /* copy all except id */ ++ /* Copy all except id. */ + memcpy(attr, b, asize); + attr->id = le_b[nb]->id; + +@@ -902,9 +878,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) + } + + /* +- * ni_ins_attr_ext +- * +- * This method adds an external attribute to the ntfs_inode. ++ * ni_ins_attr_ext - Add an external attribute to the ntfs_inode. + */ + static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + enum ATTR_TYPE type, const __le16 *name, u8 name_len, +@@ -929,8 +903,8 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + } + + /* +- * standard information and attr_list cannot be made external. +- * The Log File cannot have any external attributes ++ * Standard information and attr_list cannot be made external. ++ * The Log File cannot have any external attributes. + */ + if (type == ATTR_STD || type == ATTR_LIST || + ni->mi.rno == MFT_REC_LOG) { +@@ -938,7 +912,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + goto out; + } + +- /* Create attribute list if it is not already existed */ ++ /* Create attribute list if it is not already existed. */ + if (!ni->attr_list.size) { + err = ni_create_attr_list(ni); + if (err) +@@ -955,14 +929,14 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + if (err) + goto out; + +- /* Check each of loaded subrecord */ ++ /* Check each of loaded subrecord. */ + for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) { + mi = rb_entry(node, struct mft_inode, node); + + if (is_mft_data && + (mi_enum_attr(mi, NULL) || + vbo <= ((u64)mi->rno << sbi->record_bits))) { +- /* We can't accept this record 'case MFT's bootstrapping */ ++ /* We can't accept this record 'case MFT's bootstrapping. */ + continue; + } + if (is_mft && +@@ -976,11 +950,11 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + + if ((type != ATTR_NAME || name_len) && + mi_find_attr(mi, NULL, type, name, name_len, NULL)) { +- /* Only indexed attributes can share same record */ ++ /* Only indexed attributes can share same record. */ + continue; + } + +- /* Try to insert attribute into this subrecord */ ++ /* Try to insert attribute into this subrecord. */ + attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize, + name_off, svcn); + if (!attr) +@@ -992,7 +966,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + } + + insert_ext: +- /* We have to allocate a new child subrecord*/ ++ /* We have to allocate a new child subrecord. */ + err = ntfs_look_free_mft(sbi, &rno, is_mft_data, ni, &mi); + if (err) + goto out; +@@ -1027,9 +1001,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + } + + /* +- * ni_insert_attr +- * +- * inserts an attribute into the file. ++ * ni_insert_attr - Insert an attribute into the file. + * + * If the primary record has room, it will just insert the attribute. + * If not, it may make the attribute external. +@@ -1038,8 +1010,8 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + * + * NOTE: + * The ATTR_LIST and ATTR_STD cannot be made external. +- * This function does not fill new attribute full +- * It only fills 'size'/'type'/'id'/'name_len' fields ++ * This function does not fill new attribute full. ++ * It only fills 'size'/'type'/'id'/'name_len' fields. + */ + static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, u32 asize, +@@ -1064,7 +1036,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + free = sbi->record_size - used; + + if (is_mft && type != ATTR_LIST) { +- /* Reserve space for the ATTRIB List. */ ++ /* Reserve space for the ATTRIB list. */ + if (free < list_reserve) + free = 0; + else +@@ -1092,7 +1064,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + /* +- * Here we have: "is_mft && type == ATTR_DATA && !svcn ++ * Here we have: "is_mft && type == ATTR_DATA && !svcn" + * + * The first chunk of the $MFT::Data ATTRIB must be the base record. + * Evict as many other attributes as possible. +@@ -1111,7 +1083,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + if (max_free < asize + list_reserve) { +- /* Impossible to insert this attribute into primary record */ ++ /* Impossible to insert this attribute into primary record. */ + err = -EINVAL; + goto out; + } +@@ -1122,12 +1094,12 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + for (;;) { + attr = mi_enum_attr(&ni->mi, attr); + if (!attr) { +- /* We should never be here 'cause we have already check this case */ ++ /* We should never be here 'cause we have already check this case. */ + err = -EINVAL; + goto out; + } + +- /* Skip attributes that MUST be primary record */ ++ /* Skip attributes that MUST be primary record. */ + if (attr->type == ATTR_STD || attr->type == ATTR_LIST) + continue; + +@@ -1135,7 +1107,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (ni->attr_list.size) { + le = al_find_le(ni, NULL, attr); + if (!le) { +- /* Really this is a serious bug */ ++ /* Really this is a serious bug. */ + err = -EINVAL; + goto out; + } +@@ -1153,10 +1125,10 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + memcpy(eattr, attr, t32); + eattr->id = id; + +- /* remove attrib from primary record */ ++ /* Remove attrib from primary record. */ + mi_remove_attr(&ni->mi, attr); + +- /* attr now points to next attribute */ ++ /* attr now points to next attribute. */ + if (attr->type == ATTR_END) + goto out; + } +@@ -1179,11 +1151,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + return err; + } + +-/* +- * ni_expand_mft_list +- * +- * This method splits ATTR_DATA of $MFT +- */ ++/* ni_expand_mft_list - Split ATTR_DATA of $MFT. */ + static int ni_expand_mft_list(struct ntfs_inode *ni) + { + int err = 0; +@@ -1195,7 +1163,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + struct mft_inode *mi, *mi_min, *mi_new; + struct ntfs_sb_info *sbi = ni->mi.sbi; + +- /* Find the nearest Mft */ ++ /* Find the nearest MFT. */ + mft_min = 0; + mft_new = 0; + mi_min = NULL; +@@ -1214,7 +1182,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + + if (ntfs_look_free_mft(sbi, &mft_new, true, ni, &mi_new)) { + mft_new = 0; +- // really this is not critical ++ /* Really this is not critical. */ + } else if (mft_min > mft_new) { + mft_min = mft_new; + mi_min = mi_new; +@@ -1240,9 +1208,9 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + } + + /* +- * split primary attribute [0 evcn] in two parts [0 svcn) + [svcn evcn] ++ * Split primary attribute [0 evcn] in two parts [0 svcn) + [svcn evcn]. + * +- * Update first part of ATTR_DATA in 'primary MFT ++ * Update first part of ATTR_DATA in 'primary MFT. + */ + err = run_pack(run, 0, svcn, Add2Ptr(attr, SIZEOF_NONRESIDENT), + asize - SIZEOF_NONRESIDENT, &plen); +@@ -1259,11 +1227,11 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + + attr->nres.evcn = cpu_to_le64(svcn - 1); + attr->size = cpu_to_le32(run_size + SIZEOF_NONRESIDENT); +- /* 'done' - how many bytes of primary MFT becomes free */ ++ /* 'done' - How many bytes of primary MFT becomes free. */ + done = asize - run_size - SIZEOF_NONRESIDENT; + le32_sub_cpu(&ni->mi.mrec->used, done); + +- /* Estimate the size of second part: run_buf=NULL */ ++ /* Estimate the size of second part: run_buf=NULL. */ + err = run_pack(run, svcn, evcn + 1 - svcn, NULL, sbi->record_size, + &plen); + if (err < 0) +@@ -1278,8 +1246,8 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + } + + /* +- * This function may implicitly call expand attr_list +- * Insert second part of ATTR_DATA in 'mi_min' ++ * This function may implicitly call expand attr_list. ++ * Insert second part of ATTR_DATA in 'mi_min'. + */ + attr = ni_ins_new_attr(ni, mi_min, NULL, ATTR_DATA, NULL, 0, + SIZEOF_NONRESIDENT + run_size, +@@ -1310,9 +1278,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + } + + /* +- * ni_expand_list +- * +- * This method moves all possible attributes out of primary record ++ * ni_expand_list - Move all possible attributes out of primary record. + */ + int ni_expand_list(struct ntfs_inode *ni) + { +@@ -1336,7 +1302,7 @@ int ni_expand_list(struct ntfs_inode *ni) + if (is_mft && le->type == ATTR_DATA) + continue; + +- /* Find attribute in primary record */ ++ /* Find attribute in primary record. */ + attr = rec_find_attr_le(&ni->mi, le); + if (!attr) { + err = -EINVAL; +@@ -1345,7 +1311,7 @@ int ni_expand_list(struct ntfs_inode *ni) + + asize = le32_to_cpu(attr->size); + +- /* Always insert into new record to avoid collisions (deep recursive) */ ++ /* Always insert into new record to avoid collisions (deep recursive). */ + err = ni_ins_attr_ext(ni, le, attr->type, attr_name(attr), + attr->name_len, asize, attr_svcn(attr), + le16_to_cpu(attr->name_off), true, +@@ -1363,11 +1329,11 @@ int ni_expand_list(struct ntfs_inode *ni) + } + + if (!is_mft) { +- err = -EFBIG; /* attr list is too big(?) */ ++ err = -EFBIG; /* Attr list is too big(?) */ + goto out; + } + +- /* split mft data as much as possible */ ++ /* Split MFT data as much as possible. */ + err = ni_expand_mft_list(ni); + if (err) + goto out; +@@ -1377,9 +1343,7 @@ int ni_expand_list(struct ntfs_inode *ni) + } + + /* +- * ni_insert_nonresident +- * +- * inserts new nonresident attribute ++ * ni_insert_nonresident - Insert new nonresident attribute. + */ + int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, +@@ -1454,9 +1418,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + /* +- * ni_insert_resident +- * +- * inserts new resident attribute ++ * ni_insert_resident - Inserts new resident attribute. + */ + int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + enum ATTR_TYPE type, const __le16 *name, u8 name_len, +@@ -1488,9 +1450,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + } + + /* +- * ni_remove_attr_le +- * +- * removes attribute from record ++ * ni_remove_attr_le - Remove attribute from record. + */ + int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, + struct ATTR_LIST_ENTRY *le) +@@ -1511,10 +1471,9 @@ int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, + } + + /* +- * ni_delete_all ++ * ni_delete_all - Remove all attributes and frees allocates space. + * +- * removes all attributes and frees allocates space +- * ntfs_evict_inode->ntfs_clear_inode->ni_delete_all (if no links) ++ * ntfs_evict_inode->ntfs_clear_inode->ni_delete_all (if no links). + */ + int ni_delete_all(struct ntfs_inode *ni) + { +@@ -1553,7 +1512,7 @@ int ni_delete_all(struct ntfs_inode *ni) + asize = le32_to_cpu(attr->size); + roff = le16_to_cpu(attr->nres.run_off); + +- /*run==1 means unpack and deallocate*/ ++ /* run==1 means unpack and deallocate. */ + run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn, + Add2Ptr(attr, roff), asize - roff); + } +@@ -1563,7 +1522,7 @@ int ni_delete_all(struct ntfs_inode *ni) + al_destroy(ni); + } + +- /* Free all subrecords */ ++ /* Free all subrecords. */ + for (node = rb_first(&ni->mi_tree); node;) { + struct rb_node *next = rb_next(node); + struct mft_inode *mi = rb_entry(node, struct mft_inode, node); +@@ -1578,7 +1537,7 @@ int ni_delete_all(struct ntfs_inode *ni) + node = next; + } + +- // Free base record ++ /* Free base record */ + clear_rec_inuse(ni->mi.mrec); + ni->mi.dirty = true; + err = mi_write(&ni->mi, 0); +@@ -1588,11 +1547,9 @@ int ni_delete_all(struct ntfs_inode *ni) + return err; + } + +-/* +- * ni_fname_name ++/* ni_fname_name + * +- * returns file name attribute by its value +- */ ++ *Return: File name attribute by its value. */ + struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + const struct cpu_str *uni, + const struct MFT_REF *home_dir, +@@ -1603,7 +1560,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + + *le = NULL; + +- /* Enumerate all names */ ++ /* Enumerate all names. */ + next: + attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, NULL); + if (!attr) +@@ -1632,7 +1589,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + /* + * ni_fname_type + * +- * returns file name attribute with given type ++ * Return: File name attribute with given type. + */ + struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, + struct ATTR_LIST_ENTRY **le) +@@ -1642,7 +1599,7 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, + + *le = NULL; + +- /* Enumerate all names */ ++ /* Enumerate all names. */ + for (;;) { + attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, + NULL); +@@ -1656,9 +1613,11 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, + } + + /* +- * Process compressed/sparsed in special way +- * NOTE: you need to set ni->std_fa = new_fa +- * after this function to keep internal structures in consistency ++ * ni_new_attr_flags ++ * ++ * Process compressed/sparsed in special way. ++ * NOTE: You need to set ni->std_fa = new_fa ++ * after this function to keep internal structures in consistency. + */ + int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) + { +@@ -1703,7 +1662,7 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) + return -EOPNOTSUPP; + } + +- /* resize nonresident empty attribute in-place only*/ ++ /* Resize nonresident empty attribute in-place only. */ + new_asize = (new_aflags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) + ? (SIZEOF_NONRESIDENT_EX + 8) + : (SIZEOF_NONRESIDENT + 8); +@@ -1713,17 +1672,17 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) + + if (new_aflags & ATTR_FLAG_SPARSED) { + attr->name_off = SIZEOF_NONRESIDENT_EX_LE; +- /* windows uses 16 clusters per frame but supports one cluster per frame too*/ ++ /* Windows uses 16 clusters per frame but supports one cluster per frame too. */ + attr->nres.c_unit = 0; + ni->vfs_inode.i_mapping->a_ops = &ntfs_aops; + } else if (new_aflags & ATTR_FLAG_COMPRESSED) { + attr->name_off = SIZEOF_NONRESIDENT_EX_LE; +- /* the only allowed: 16 clusters per frame */ ++ /* The only allowed: 16 clusters per frame. */ + attr->nres.c_unit = NTFS_LZNT_CUNIT; + ni->vfs_inode.i_mapping->a_ops = &ntfs_aops_cmpr; + } else { + attr->name_off = SIZEOF_NONRESIDENT_LE; +- /* normal files */ ++ /* Normal files. */ + attr->nres.c_unit = 0; + ni->vfs_inode.i_mapping->a_ops = &ntfs_aops; + } +@@ -1738,7 +1697,7 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) + /* + * ni_parse_reparse + * +- * buffer is at least 24 bytes ++ * Buffer is at least 24 bytes. + */ + enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + void *buffer) +@@ -1750,7 +1709,7 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + + static_assert(sizeof(struct REPARSE_DATA_BUFFER) <= 24); + +- /* Try to estimate reparse point */ ++ /* Try to estimate reparse point. */ + if (!attr->non_res) { + rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER)); + } else if (le64_to_cpu(attr->nres.data_size) >= +@@ -1775,9 +1734,9 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + len = le16_to_cpu(rp->ReparseDataLength); + switch (rp->ReparseTag) { + case (IO_REPARSE_TAG_MICROSOFT | IO_REPARSE_TAG_SYMBOLIC_LINK): +- break; /* Symbolic link */ ++ break; /* Symbolic link. */ + case IO_REPARSE_TAG_MOUNT_POINT: +- break; /* Mount points and junctions */ ++ break; /* Mount points and junctions. */ + case IO_REPARSE_TAG_SYMLINK: + break; + case IO_REPARSE_TAG_COMPRESS: +@@ -1835,14 +1794,15 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + return REPARSE_NONE; + } + +- /* Looks like normal symlink */ ++ /* Looks like normal symlink. */ + return REPARSE_LINK; + } + + /* +- * helper for file_fiemap +- * assumed ni_lock +- * TODO: less aggressive locks ++ * ni_fiemap - Helper for file_fiemap(). ++ * ++ * Assumed ni_lock. ++ * TODO: Less aggressive locks. + */ + int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, + __u64 vbo, __u64 len) +@@ -1876,7 +1836,7 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, + goto out; + } + if (is_attr_compressed(attr)) { +- /*unfortunately cp -r incorrectly treats compressed clusters*/ ++ /* Unfortunately cp -r incorrectly treats compressed clusters. */ + err = -EOPNOTSUPP; + ntfs_inode_warn( + &ni->vfs_inode, +@@ -2018,6 +1978,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, + } + + /* ++ * ni_readpage_cmpr ++ * + * When decompressing, we typically obtain more than one page per reference. + * We inject the additional pages into the page cache. + */ +@@ -2028,7 +1990,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + struct address_space *mapping = page->mapping; + pgoff_t index = page->index; + u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT; +- struct page **pages = NULL; /*array of at most 16 pages. stack?*/ ++ struct page **pages = NULL; /* Array of at most 16 pages. stack? */ + u8 frame_bits; + CLST frame; + u32 i, idx, frame_size, pages_per_frame; +@@ -2042,10 +2004,10 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + } + + if (ni->ni_flags & NI_FLAG_COMPRESSED_MASK) { +- /* xpress or lzx */ ++ /* Xpress or LZX. */ + frame_bits = ni_ext_compress_bits(ni); + } else { +- /* lznt compression*/ ++ /* LZNT compression. */ + frame_bits = NTFS_LZNT_CUNIT + sbi->cluster_bits; + } + frame_size = 1u << frame_bits; +@@ -2091,7 +2053,7 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + } + + out: +- /* At this point, err contains 0 or -EIO depending on the "critical" page */ ++ /* At this point, err contains 0 or -EIO depending on the "critical" page. */ + kfree(pages); + unlock_page(page); + +@@ -2100,9 +2062,10 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct page *page) + + #ifdef CONFIG_NTFS3_LZX_XPRESS + /* +- * decompress lzx/xpress compressed file +- * remove ATTR_DATA::WofCompressedData +- * remove ATTR_REPARSE ++ * ni_decompress_file - Decompress LZX/Xpress compressed file. ++ * ++ * Remove ATTR_DATA::WofCompressedData. ++ * Remove ATTR_REPARSE. + */ + int ni_decompress_file(struct ntfs_inode *ni) + { +@@ -2122,13 +2085,13 @@ int ni_decompress_file(struct ntfs_inode *ni) + struct mft_inode *mi; + int err; + +- /* clusters for decompressed data*/ ++ /* Clusters for decompressed data. */ + cend = bytes_to_cluster(sbi, i_size); + + if (!i_size) + goto remove_wof; + +- /* check in advance */ ++ /* Check in advance. */ + if (cend > wnd_zeroes(&sbi->used.bitmap)) { + err = -ENOSPC; + goto out; +@@ -2144,7 +2107,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + } + + /* +- * Step 1: decompress data and copy to new allocated clusters ++ * Step 1: Decompress data and copy to new allocated clusters. + */ + index = 0; + for (vbo = 0; vbo < i_size; vbo += bytes) { +@@ -2206,7 +2169,8 @@ int ni_decompress_file(struct ntfs_inode *ni) + + remove_wof: + /* +- * Step 2: deallocate attributes ATTR_DATA::WofCompressedData and ATTR_REPARSE ++ * Step 2: Deallocate attributes ATTR_DATA::WofCompressedData ++ * and ATTR_REPARSE. + */ + attr = NULL; + le = NULL; +@@ -2239,13 +2203,13 @@ int ni_decompress_file(struct ntfs_inode *ni) + asize = le32_to_cpu(attr->size); + roff = le16_to_cpu(attr->nres.run_off); + +- /*run==1 means unpack and deallocate*/ ++ /*run==1 Means unpack and deallocate. */ + run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, evcn, svcn, + Add2Ptr(attr, roff), asize - roff); + } + + /* +- * Step 3: remove attribute ATTR_DATA::WofCompressedData ++ * Step 3: Remove attribute ATTR_DATA::WofCompressedData. + */ + err = ni_remove_attr(ni, ATTR_DATA, WOF_NAME, ARRAY_SIZE(WOF_NAME), + false, NULL); +@@ -2253,14 +2217,14 @@ int ni_decompress_file(struct ntfs_inode *ni) + goto out; + + /* +- * Step 4: remove ATTR_REPARSE ++ * Step 4: Remove ATTR_REPARSE. + */ + err = ni_remove_attr(ni, ATTR_REPARSE, NULL, 0, false, NULL); + if (err) + goto out; + + /* +- * Step 5: remove sparse flag from data attribute ++ * Step 5: Remove sparse flag from data attribute. + */ + attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, &mi); + if (!attr) { +@@ -2269,7 +2233,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + } + + if (attr->non_res && is_attr_sparsed(attr)) { +- /* sparsed attribute header is 8 bytes bigger than normal*/ ++ /* Sarsed attribute header is 8 bytes bigger than normal. */ + struct MFT_REC *rec = mi->mrec; + u32 used = le32_to_cpu(rec->used); + u32 asize = le32_to_cpu(attr->size); +@@ -2289,7 +2253,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + mark_inode_dirty(inode); + } + +- /* clear cached flag */ ++ /* Clear cached flag. */ + ni->ni_flags &= ~NI_FLAG_COMPRESSED_MASK; + if (ni->file.offs_page) { + put_page(ni->file.offs_page); +@@ -2307,7 +2271,9 @@ int ni_decompress_file(struct ntfs_inode *ni) + return err; + } + +-/* external compression lzx/xpress */ ++/* ++ * decompress_lzx_xpress - External compression LZX/Xpress. ++ */ + static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + size_t cmpr_size, void *unc, size_t unc_size, + u32 frame_size) +@@ -2316,7 +2282,7 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + void *ctx; + + if (cmpr_size == unc_size) { +- /* frame not compressed */ ++ /* Frame not compressed. */ + memcpy(unc, cmpr, unc_size); + return 0; + } +@@ -2324,10 +2290,10 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + err = 0; + if (frame_size == 0x8000) { + mutex_lock(&sbi->compress.mtx_lzx); +- /* LZX: frame compressed */ ++ /* LZX: Frame compressed. */ + ctx = sbi->compress.lzx; + if (!ctx) { +- /* Lazy initialize lzx decompress context */ ++ /* Lazy initialize LZX decompress context. */ + ctx = lzx_allocate_decompressor(); + if (!ctx) { + err = -ENOMEM; +@@ -2338,17 +2304,17 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + } + + if (lzx_decompress(ctx, cmpr, cmpr_size, unc, unc_size)) { +- /* treat all errors as "invalid argument" */ ++ /* Treat all errors as "invalid argument". */ + err = -EINVAL; + } + out1: + mutex_unlock(&sbi->compress.mtx_lzx); + } else { +- /* XPRESS: frame compressed */ ++ /* XPRESS: Frame compressed. */ + mutex_lock(&sbi->compress.mtx_xpress); + ctx = sbi->compress.xpress; + if (!ctx) { +- /* Lazy initialize xpress decompress context */ ++ /* Lazy initialize Xpress decompress context */ + ctx = xpress_allocate_decompressor(); + if (!ctx) { + err = -ENOMEM; +@@ -2359,7 +2325,7 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + } + + if (xpress_decompress(ctx, cmpr, cmpr_size, unc, unc_size)) { +- /* treat all errors as "invalid argument" */ ++ /* Treat all errors as "invalid argument". */ + err = -EINVAL; + } + out2: +@@ -2372,7 +2338,7 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + /* + * ni_read_frame + * +- * pages - array of locked pages ++ * Pages - array of locked pages. + */ + int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + u32 pages_per_frame) +@@ -2394,7 +2360,8 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + CLST frame, clst_data; + + /* +- * To simplify decompress algorithm do vmap for source and target pages ++ * To simplify decompress algorithm do vmap for source ++ * and target pages. + */ + for (i = 0; i < pages_per_frame; i++) + kmap(pages[i]); +@@ -2451,7 +2418,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + case 0x8000: + break; + default: +- /* unknown compression */ ++ /* Unknown compression. */ + err = -EOPNOTSUPP; + goto out1; + } +@@ -2509,7 +2476,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + goto out1; + } + vbo_disk = vbo_data; +- /* load all runs to read [vbo_disk-vbo_to) */ ++ /* Load all runs to read [vbo_disk-vbo_to). */ + err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME, + ARRAY_SIZE(WOF_NAME), run, vbo_disk, + vbo_data + ondisk_size); +@@ -2520,7 +2487,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + PAGE_SHIFT; + #endif + } else if (is_attr_compressed(attr)) { +- /* lznt compression*/ ++ /* LZNT compression. */ + if (sbi->cluster_size > NTFS_LZNT_MAX_CLUSTER) { + err = -EOPNOTSUPP; + goto out1; +@@ -2548,7 +2515,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + ondisk_size = clst_data << cluster_bits; + + if (clst_data >= NTFS_LZNT_CLUSTERS) { +- /* frame is not compressed */ ++ /* Frame is not compressed. */ + down_read(&ni->file.run_lock); + err = ntfs_bio_pages(sbi, run, pages, pages_per_frame, + frame_vbo, ondisk_size, +@@ -2581,7 +2548,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + kmap(pg); + } + +- /* read 'ondisk_size' bytes from disk */ ++ /* Read 'ondisk_size' bytes from disk. */ + down_read(&ni->file.run_lock); + err = ntfs_bio_pages(sbi, run, pages_disk, npages_disk, vbo_disk, + ondisk_size, REQ_OP_READ); +@@ -2590,7 +2557,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + goto out3; + + /* +- * To simplify decompress algorithm do vmap for source and target pages ++ * To simplify decompress algorithm do vmap for source and target pages. + */ + frame_ondisk = vmap(pages_disk, npages_disk, VM_MAP, PAGE_KERNEL_RO); + if (!frame_ondisk) { +@@ -2598,7 +2565,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + goto out3; + } + +- /* decompress: frame_ondisk -> frame_mem */ ++ /* Decompress: Frame_ondisk -> frame_mem. */ + #ifdef CONFIG_NTFS3_LZX_XPRESS + if (run != &ni->file.run) { + /* LZX or XPRESS */ +@@ -2608,7 +2575,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + } else + #endif + { +- /* LZNT - native ntfs compression */ ++ /* LZNT - Native NTFS compression. */ + unc_size = decompress_lznt(frame_ondisk, ondisk_size, frame_mem, + frame_size); + if ((ssize_t)unc_size < 0) +@@ -2656,7 +2623,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + /* + * ni_write_frame + * +- * pages - array of locked pages ++ * Pages - Array of locked pages. + */ + int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + u32 pages_per_frame) +@@ -2726,9 +2693,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + kmap(pg); + } + +- /* +- * To simplify compress algorithm do vmap for source and target pages +- */ ++ /* To simplify compress algorithm do vmap for source and target pages. */ + frame_ondisk = vmap(pages_disk, pages_per_frame, VM_MAP, PAGE_KERNEL); + if (!frame_ondisk) { + err = -ENOMEM; +@@ -2738,7 +2703,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + for (i = 0; i < pages_per_frame; i++) + kmap(pages[i]); + +- /* map in-memory frame for read-only */ ++ /* Map in-memory frame for read-only. */ + frame_mem = vmap(pages, pages_per_frame, VM_MAP, PAGE_KERNEL_RO); + if (!frame_mem) { + err = -ENOMEM; +@@ -2749,9 +2714,9 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + lznt = NULL; + if (!sbi->compress.lznt) { + /* +- * lznt implements two levels of compression: +- * 0 - standard compression +- * 1 - best compression, requires a lot of cpu ++ * LZNT implements two levels of compression: ++ * 0 - Standard compression ++ * 1 - Best compression, requires a lot of cpu + * use mount option? + */ + lznt = get_lznt_ctx(0); +@@ -2765,22 +2730,22 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + lznt = NULL; + } + +- /* compress: frame_mem -> frame_ondisk */ ++ /* Compress: frame_mem -> frame_ondisk. */ + compr_size = compress_lznt(frame_mem, frame_size, frame_ondisk, + frame_size, sbi->compress.lznt); + mutex_unlock(&sbi->compress.mtx_lznt); + kfree(lznt); + + if (compr_size + sbi->cluster_size > frame_size) { +- /* frame is not compressed */ ++ /* Frame is not compressed. */ + compr_size = frame_size; + ondisk_size = frame_size; + } else if (compr_size) { +- /* frame is compressed */ ++ /* Frame is compressed. */ + ondisk_size = ntfs_up_cluster(sbi, compr_size); + memset(frame_ondisk + compr_size, 0, ondisk_size - compr_size); + } else { +- /* frame is sparsed */ ++ /* Frame is sparsed. */ + ondisk_size = 0; + } + +@@ -2824,7 +2789,9 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + } + + /* +- * update duplicate info of ATTR_FILE_NAME in MFT and in parent directories ++ * ni_update_parent ++ * ++ * Update duplicate info of ATTR_FILE_NAME in MFT and in parent directories. + */ + static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + int sync) +@@ -2876,7 +2843,7 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + } + } + +- /* TODO: fill reparse info */ ++ /* TODO: Fill reparse info. */ + dup->reparse = 0; + dup->ea_size = 0; + +@@ -2911,9 +2878,9 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + } + + if (!active) +- continue; /*avoid __wait_on_freeing_inode(inode); */ ++ continue; /* Avoid __wait_on_freeing_inode(inode); */ + +- /*ntfs_iget5 may sleep*/ ++ /* ntfs_iget5 may sleep. */ + dir = ntfs_iget5(sb, &fname->home, NULL); + if (IS_ERR(dir)) { + ntfs_inode_warn( +@@ -2940,9 +2907,7 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + } + + /* +- * ni_write_inode +- * +- * write mft base record and all subrecords to disk ++ * ni_write_inode - Write MFT base record and all subrecords to disk. + */ + int ni_write_inode(struct inode *inode, int sync, const char *hint) + { +@@ -2959,7 +2924,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + return 0; + + if (!ni_trylock(ni)) { +- /* 'ni' is under modification, skip for now */ ++ /* 'ni' is under modification, skip for now. */ + mark_inode_dirty_sync(inode); + return 0; + } +@@ -2968,7 +2933,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + !(sbi->flags & NTFS_FLAGS_LOG_REPLAYING) && inode->i_nlink) { + bool modified = false; + +- /* update times in standard attribute */ ++ /* Update times in standard attribute. */ + std = ni_std(ni); + if (!std) { + err = -EINVAL; +@@ -3006,7 +2971,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + if (!ntfs_is_meta_file(sbi, inode->i_ino) && + (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))) { + dup.cr_time = std->cr_time; +- /* Not critical if this function fail */ ++ /* Not critical if this function fail. */ + re_dirty = ni_update_parent(ni, &dup, sync); + + if (re_dirty) +@@ -3015,7 +2980,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + ni->ni_flags &= ~NI_FLAG_UPDATE_PARENT; + } + +- /* update attribute list */ ++ /* Update attribute list. */ + if (ni->attr_list.size && ni->attr_list.dirty) { + if (inode->i_ino != MFT_REC_MFT || sync) { + err = ni_try_remove_attr_list(ni); +diff --git a/src/fslog.c b/src/fslog.c +index 7144ea8a9ab898e35c75e06192f20f9d93504bb8..6f6057129fddd602ce0f86cd2aafba6b5c9fa356 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -30,8 +30,8 @@ + + struct RESTART_HDR { + struct NTFS_RECORD_HEADER rhdr; // 'RSTR' +- __le32 sys_page_size; // 0x10: Page size of the system which initialized the log +- __le32 page_size; // 0x14: Log page size used for this log file ++ __le32 sys_page_size; // 0x10: Page size of the system which initialized the log. ++ __le32 page_size; // 0x14: Log page size used for this log file. + __le16 ra_off; // 0x18: + __le16 minor_ver; // 0x1A: + __le16 major_ver; // 0x1C: +@@ -47,26 +47,26 @@ struct CLIENT_REC { + __le16 prev_client; // 0x10: + __le16 next_client; // 0x12: + __le16 seq_num; // 0x14: +- u8 align[6]; // 0x16 +- __le32 name_bytes; // 0x1C: in bytes +- __le16 name[32]; // 0x20: name of client ++ u8 align[6]; // 0x16: ++ __le32 name_bytes; // 0x1C: In bytes. ++ __le16 name[32]; // 0x20: Name of client. + }; + + static_assert(sizeof(struct CLIENT_REC) == 0x60); + + /* Two copies of these will exist at the beginning of the log file */ + struct RESTART_AREA { +- __le64 current_lsn; // 0x00: Current logical end of log file +- __le16 log_clients; // 0x08: Maximum number of clients +- __le16 client_idx[2]; // 0x0A: free/use index into the client record arrays +- __le16 flags; // 0x0E: See RESTART_SINGLE_PAGE_IO +- __le32 seq_num_bits; // 0x10: the number of bits in sequence number. ++ __le64 current_lsn; // 0x00: Current logical end of log file. ++ __le16 log_clients; // 0x08: Maximum number of clients. ++ __le16 client_idx[2]; // 0x0A: Free/use index into the client record arrays. ++ __le16 flags; // 0x0E: See RESTART_SINGLE_PAGE_IO. ++ __le32 seq_num_bits; // 0x10: The number of bits in sequence number. + __le16 ra_len; // 0x14: + __le16 client_off; // 0x16: + __le64 l_size; // 0x18: Usable log file size. + __le32 last_lsn_data_len; // 0x20: +- __le16 rec_hdr_len; // 0x24: log page data offset +- __le16 data_off; // 0x26: log page data length ++ __le16 rec_hdr_len; // 0x24: Log page data offset. ++ __le16 data_off; // 0x26: Log page data length. + __le32 open_log_count; // 0x28: + __le32 align[5]; // 0x2C: + struct CLIENT_REC clients[]; // 0x40: +@@ -75,10 +75,10 @@ struct RESTART_AREA { + struct LOG_REC_HDR { + __le16 redo_op; // 0x00: NTFS_LOG_OPERATION + __le16 undo_op; // 0x02: NTFS_LOG_OPERATION +- __le16 redo_off; // 0x04: Offset to Redo record +- __le16 redo_len; // 0x06: Redo length +- __le16 undo_off; // 0x08: Offset to Undo record +- __le16 undo_len; // 0x0A: Undo length ++ __le16 redo_off; // 0x04: Offset to Redo record. ++ __le16 redo_len; // 0x06: Redo length. ++ __le16 undo_off; // 0x08: Offset to Undo record. ++ __le16 undo_len; // 0x0A: Undo length. + __le16 target_attr; // 0x0C: + __le16 lcns_follow; // 0x0E: + __le16 record_off; // 0x10: +@@ -95,20 +95,20 @@ static_assert(sizeof(struct LOG_REC_HDR) == 0x20); + #define RESTART_ENTRY_ALLOCATED_LE cpu_to_le32(0xFFFFFFFF) + + struct RESTART_TABLE { +- __le16 size; // 0x00: In bytes +- __le16 used; // 0x02: entries +- __le16 total; // 0x04: entries ++ __le16 size; // 0x00: In bytes ++ __le16 used; // 0x02: Entries ++ __le16 total; // 0x04: Entries + __le16 res[3]; // 0x06: + __le32 free_goal; // 0x0C: +- __le32 first_free; // 0x10 +- __le32 last_free; // 0x14 ++ __le32 first_free; // 0x10: ++ __le32 last_free; // 0x14: + + }; + + static_assert(sizeof(struct RESTART_TABLE) == 0x18); + + struct ATTR_NAME_ENTRY { +- __le16 off; // offset in the Open attribute Table ++ __le16 off; // Offset in the Open attribute Table. + __le16 name_bytes; + __le16 name[]; + }; +@@ -121,7 +121,7 @@ struct OPEN_ATTR_ENRTY { + u8 is_attr_name; // 0x0B: Faked field to manage 'ptr' + u8 name_len; // 0x0C: Faked field to manage 'ptr' + u8 res; +- struct MFT_REF ref; // 0x10: File Reference of file containing attribute ++ struct MFT_REF ref; // 0x10: File Reference of file containing attribute + __le64 open_record_lsn; // 0x18: + void *ptr; // 0x20: + }; +@@ -133,10 +133,10 @@ struct OPEN_ATTR_ENRTY_32 { + struct MFT_REF ref; // 0x08: + __le64 open_record_lsn; // 0x10: + u8 is_dirty_pages; // 0x18: +- u8 is_attr_name; // 0x19 ++ u8 is_attr_name; // 0x19: + u8 res1[2]; + enum ATTR_TYPE type; // 0x1C: +- u8 name_len; // 0x20: in wchar ++ u8 name_len; // 0x20: In wchar + u8 res2[3]; + __le32 AttributeName; // 0x24: + __le32 bytes_per_index; // 0x28: +@@ -147,15 +147,15 @@ struct OPEN_ATTR_ENRTY_32 { + static_assert(sizeof(struct OPEN_ATTR_ENRTY) < SIZEOF_OPENATTRIBUTEENTRY0); + + /* +- * One entry exists in the Dirty Pages Table for each page which is dirty at the +- * time the Restart Area is written ++ * One entry exists in the Dirty Pages Table for each page which is dirty at ++ * the time the Restart Area is written. + */ + struct DIR_PAGE_ENTRY { +- __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated +- __le32 target_attr; // 0x04: Index into the Open attribute Table ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ __le32 target_attr; // 0x04: Index into the Open attribute Table + __le32 transfer_len; // 0x08: + __le32 lcns_follow; // 0x0C: +- __le64 vcn; // 0x10: Vcn of dirty page ++ __le64 vcn; // 0x10: Vcn of dirty page + __le64 oldest_lsn; // 0x18: + __le64 page_lcns[]; // 0x20: + }; +@@ -164,17 +164,17 @@ static_assert(sizeof(struct DIR_PAGE_ENTRY) == 0x20); + + /* 32 bit version of 'struct DIR_PAGE_ENTRY' */ + struct DIR_PAGE_ENTRY_32 { +- __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated +- __le32 target_attr; // 0x04: Index into the Open attribute Table +- __le32 transfer_len; // 0x08: +- __le32 lcns_follow; // 0x0C: +- __le32 reserved; // 0x10: +- __le32 vcn_low; // 0x14: Vcn of dirty page +- __le32 vcn_hi; // 0x18: Vcn of dirty page +- __le32 oldest_lsn_low; // 0x1C: +- __le32 oldest_lsn_hi; // 0x1C: +- __le32 page_lcns_low; // 0x24: +- __le32 page_lcns_hi; // 0x24: ++ __le32 next; // 0x00: RESTART_ENTRY_ALLOCATED if allocated ++ __le32 target_attr; // 0x04: Index into the Open attribute Table ++ __le32 transfer_len; // 0x08: ++ __le32 lcns_follow; // 0x0C: ++ __le32 reserved; // 0x10: ++ __le32 vcn_low; // 0x14: Vcn of dirty page ++ __le32 vcn_hi; // 0x18: Vcn of dirty page ++ __le32 oldest_lsn_low; // 0x1C: ++ __le32 oldest_lsn_hi; // 0x1C: ++ __le32 page_lcns_low; // 0x24: ++ __le32 page_lcns_hi; // 0x24: + }; + + static_assert(offsetof(struct DIR_PAGE_ENTRY_32, vcn_low) == 0x14); +@@ -233,27 +233,27 @@ struct LCN_RANGE { + __le64 len; + }; + +-/* The following type defines the different log record types */ ++/* The following type defines the different log record types. */ + #define LfsClientRecord cpu_to_le32(1) + #define LfsClientRestart cpu_to_le32(2) + +-/* This is used to uniquely identify a client for a particular log file */ ++/* This is used to uniquely identify a client for a particular log file. */ + struct CLIENT_ID { + __le16 seq_num; + __le16 client_idx; + }; + +-/* This is the header that begins every Log Record in the log file */ ++/* This is the header that begins every Log Record in the log file. */ + struct LFS_RECORD_HDR { +- __le64 this_lsn; // 0x00: +- __le64 client_prev_lsn; // 0x08: +- __le64 client_undo_next_lsn; // 0x10: +- __le32 client_data_len; // 0x18: +- struct CLIENT_ID client; // 0x1C: Owner of this log record +- __le32 record_type; // 0x20: LfsClientRecord or LfsClientRestart +- __le32 transact_id; // 0x24: +- __le16 flags; // 0x28: LOG_RECORD_MULTI_PAGE +- u8 align[6]; // 0x2A: ++ __le64 this_lsn; // 0x00: ++ __le64 client_prev_lsn; // 0x08: ++ __le64 client_undo_next_lsn; // 0x10: ++ __le32 client_data_len; // 0x18: ++ struct CLIENT_ID client; // 0x1C: Owner of this log record. ++ __le32 record_type; // 0x20: LfsClientRecord or LfsClientRestart. ++ __le32 transact_id; // 0x24: ++ __le16 flags; // 0x28: LOG_RECORD_MULTI_PAGE ++ u8 align[6]; // 0x2A: + }; + + #define LOG_RECORD_MULTI_PAGE cpu_to_le16(1) +@@ -261,26 +261,26 @@ struct LFS_RECORD_HDR { + static_assert(sizeof(struct LFS_RECORD_HDR) == 0x30); + + struct LFS_RECORD { +- __le16 next_record_off; // 0x00: Offset of the free space in the page +- u8 align[6]; // 0x02: +- __le64 last_end_lsn; // 0x08: lsn for the last log record which ends on the page ++ __le16 next_record_off; // 0x00: Offset of the free space in the page, ++ u8 align[6]; // 0x02: ++ __le64 last_end_lsn; // 0x08: lsn for the last log record which ends on the page, + }; + + static_assert(sizeof(struct LFS_RECORD) == 0x10); + + struct RECORD_PAGE_HDR { +- struct NTFS_RECORD_HEADER rhdr; // 'RCRD' +- __le32 rflags; // 0x10: See LOG_PAGE_LOG_RECORD_END +- __le16 page_count; // 0x14: +- __le16 page_pos; // 0x16: +- struct LFS_RECORD record_hdr; // 0x18 +- __le16 fixups[10]; // 0x28 +- __le32 file_off; // 0x3c: used when major version >= 2 ++ struct NTFS_RECORD_HEADER rhdr; // 'RCRD' ++ __le32 rflags; // 0x10: See LOG_PAGE_LOG_RECORD_END ++ __le16 page_count; // 0x14: ++ __le16 page_pos; // 0x16: ++ struct LFS_RECORD record_hdr; // 0x18: ++ __le16 fixups[10]; // 0x28: ++ __le32 file_off; // 0x3c: Used when major version >= 2 + }; + + // clang-format on + +-// Page contains the end of a log record ++// Page contains the end of a log record. + #define LOG_PAGE_LOG_RECORD_END cpu_to_le32(0x00000001) + + static inline bool is_log_record_end(const struct RECORD_PAGE_HDR *hdr) +@@ -294,7 +294,7 @@ static_assert(offsetof(struct RECORD_PAGE_HDR, file_off) == 0x3c); + * END of NTFS LOG structures + */ + +-/* Define some tuning parameters to keep the restart tables a reasonable size */ ++/* Define some tuning parameters to keep the restart tables a reasonable size. */ + #define INITIAL_NUMBER_TRANSACTIONS 5 + + enum NTFS_LOG_OPERATION { +@@ -342,8 +342,9 @@ enum NTFS_LOG_OPERATION { + }; + + /* +- * Array for log records which require a target attribute +- * A true indicates that the corresponding restart operation requires a target attribute ++ * Array for log records which require a target attribute. ++ * A true indicates that the corresponding restart operation ++ * requires a target attribute. + */ + static const u8 AttributeRequired[] = { + 0xFC, 0xFB, 0xFF, 0x10, 0x06, +@@ -380,14 +381,14 @@ static inline bool can_skip_action(enum NTFS_LOG_OPERATION op) + + enum { lcb_ctx_undo_next, lcb_ctx_prev, lcb_ctx_next }; + +-/* bytes per restart table */ ++/* Bytes per restart table. */ + static inline u32 bytes_per_rt(const struct RESTART_TABLE *rt) + { + return le16_to_cpu(rt->used) * le16_to_cpu(rt->size) + + sizeof(struct RESTART_TABLE); + } + +-/* log record length */ ++/* Log record length. */ + static inline u32 lrh_length(const struct LOG_REC_HDR *lr) + { + u16 t16 = le16_to_cpu(lr->lcns_follow); +@@ -396,11 +397,11 @@ static inline u32 lrh_length(const struct LOG_REC_HDR *lr) + } + + struct lcb { +- struct LFS_RECORD_HDR *lrh; // Log record header of the current lsn ++ struct LFS_RECORD_HDR *lrh; // Log record header of the current lsn. + struct LOG_REC_HDR *log_rec; + u32 ctx_mode; // lcb_ctx_undo_next/lcb_ctx_prev/lcb_ctx_next + struct CLIENT_ID client; +- bool alloc; // if true the we should deallocate 'log_rec' ++ bool alloc; // If true the we should deallocate 'log_rec'. + }; + + static void lcb_put(struct lcb *lcb) +@@ -411,11 +412,7 @@ static void lcb_put(struct lcb *lcb) + kfree(lcb); + } + +-/* +- * oldest_client_lsn +- * +- * find the oldest lsn from active clients. +- */ ++/* Find the oldest lsn from active clients. */ + static inline void oldest_client_lsn(const struct CLIENT_REC *ca, + __le16 next_client, u64 *oldest_lsn) + { +@@ -423,7 +420,7 @@ static inline void oldest_client_lsn(const struct CLIENT_REC *ca, + const struct CLIENT_REC *cr = ca + le16_to_cpu(next_client); + u64 lsn = le64_to_cpu(cr->oldest_lsn); + +- /* ignore this block if it's oldest lsn is 0 */ ++ /* Ignore this block if it's oldest lsn is 0. */ + if (lsn && lsn < *oldest_lsn) + *oldest_lsn = lsn; + +@@ -444,11 +441,11 @@ static inline bool is_rst_page_hdr_valid(u32 file_off, + return false; + } + +- /* Check that if the file offset isn't 0, it is the system page size */ ++ /* Check that if the file offset isn't 0, it is the system page size. */ + if (file_off && file_off != sys_page) + return false; + +- /* Check support version 1.1+ */ ++ /* Check support version 1.1+. */ + if (le16_to_cpu(rhdr->major_ver) <= 1 && !rhdr->minor_ver) + return false; + +@@ -498,7 +495,7 @@ static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) + + /* + * Check the restart length field and whether the entire +- * restart area is contained that length ++ * restart area is contained that length. + */ + if (le16_to_cpu(rhdr->ra_off) + le16_to_cpu(ra->ra_len) > sys_page || + off > le16_to_cpu(ra->ra_len)) { +@@ -507,7 +504,7 @@ static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) + + /* + * As a final check make sure that the use list and the free list +- * are either empty or point to a valid client ++ * are either empty or point to a valid client. + */ + fl = le16_to_cpu(ra->client_idx[0]); + ul = le16_to_cpu(ra->client_idx[1]); +@@ -515,7 +512,7 @@ static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) + (ul != LFS_NO_CLIENT && ul >= cl)) + return false; + +- /* Make sure the sequence number bits match the log file size */ ++ /* Make sure the sequence number bits match the log file size. */ + l_size = le64_to_cpu(ra->l_size); + + file_dat_bits = sizeof(u64) * 8 - le32_to_cpu(ra->seq_num_bits); +@@ -525,7 +522,7 @@ static inline bool is_rst_area_valid(const struct RESTART_HDR *rhdr) + return false; + } + +- /* The log page data offset and record header length must be quad-aligned */ ++ /* The log page data offset and record header length must be quad-aligned. */ + if (!IS_ALIGNED(le16_to_cpu(ra->data_off), 8) || + !IS_ALIGNED(le16_to_cpu(ra->rec_hdr_len), 8)) + return false; +@@ -545,13 +542,13 @@ static inline bool is_client_area_valid(const struct RESTART_HDR *rhdr, + if (usa_error && ra_len + ro > SECTOR_SIZE - sizeof(short)) + return false; + +- /* Find the start of the client array */ ++ /* Find the start of the client array. */ + ca = Add2Ptr(ra, le16_to_cpu(ra->client_off)); + + /* +- * Start with the free list +- * Check that all the clients are valid and that there isn't a cycle +- * Do the in-use list on the second pass ++ * Start with the free list. ++ * Check that all the clients are valid and that there isn't a cycle. ++ * Do the in-use list on the second pass. + */ + for (i = 0; i < 2; i++) { + u16 client_idx = le16_to_cpu(ra->client_idx[i]); +@@ -584,7 +581,7 @@ static inline bool is_client_area_valid(const struct RESTART_HDR *rhdr, + /* + * remove_client + * +- * remove a client record from a client record list an restart area ++ * Remove a client record from a client record list an restart area. + */ + static inline void remove_client(struct CLIENT_REC *ca, + const struct CLIENT_REC *cr, __le16 *head) +@@ -599,9 +596,7 @@ static inline void remove_client(struct CLIENT_REC *ca, + } + + /* +- * add_client +- * +- * add a client record to the start of a list ++ * add_client - Add a client record to the start of a list. + */ + static inline void add_client(struct CLIENT_REC *ca, u16 index, __le16 *head) + { +@@ -616,10 +611,6 @@ static inline void add_client(struct CLIENT_REC *ca, u16 index, __le16 *head) + *head = cpu_to_le16(index); + } + +-/* +- * enum_rstbl +- * +- */ + static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) + { + __le32 *e; +@@ -634,7 +625,7 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) + e = Add2Ptr(c, rsize); + } + +- /* Loop until we hit the first one allocated, or the end of the list */ ++ /* Loop until we hit the first one allocated, or the end of the list. */ + for (bprt = bytes_per_rt(t); PtrOffset(t, e) < bprt; + e = Add2Ptr(e, rsize)) { + if (*e == RESTART_ENTRY_ALLOCATED_LE) +@@ -644,9 +635,7 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) + } + + /* +- * find_dp +- * +- * searches for a 'vcn' in Dirty Page Table, ++ * find_dp - Search for a @vcn in Dirty Page Table. + */ + static inline struct DIR_PAGE_ENTRY *find_dp(struct RESTART_TABLE *dptbl, + u32 target_attr, u64 vcn) +@@ -670,10 +659,10 @@ static inline u32 norm_file_page(u32 page_size, u32 *l_size, bool use_default) + if (use_default) + page_size = DefaultLogPageSize; + +- /* Round the file size down to a system page boundary */ ++ /* Round the file size down to a system page boundary. */ + *l_size &= ~(page_size - 1); + +- /* File should contain at least 2 restart pages and MinLogRecordPages pages */ ++ /* File should contain at least 2 restart pages and MinLogRecordPages pages. */ + if (*l_size < (MinLogRecordPages + 2) * page_size) + return 0; + +@@ -743,8 +732,9 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) + return false; + } + +- /* Verify each entry is either allocated or points +- * to a valid offset the table ++ /* ++ * Verify each entry is either allocated or points ++ * to a valid offset the table. + */ + for (i = 0; i < ne; i++) { + off = le32_to_cpu(*(__le32 *)Add2Ptr( +@@ -757,8 +747,9 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) + } + } + +- /* Walk through the list headed by the first entry to make +- * sure none of the entries are currently being used ++ /* ++ * Walk through the list headed by the first entry to make ++ * sure none of the entries are currently being used. + */ + for (off = ff; off;) { + if (off == RESTART_ENTRY_ALLOCATED) +@@ -771,9 +762,7 @@ static bool check_rstbl(const struct RESTART_TABLE *rt, size_t bytes) + } + + /* +- * free_rsttbl_idx +- * +- * frees a previously allocated index a Restart Table. ++ * free_rsttbl_idx - Free a previously allocated index a Restart Table. + */ + static inline void free_rsttbl_idx(struct RESTART_TABLE *rt, u32 off) + { +@@ -863,7 +852,7 @@ static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl, + /* + * alloc_rsttbl_idx + * +- * allocates an index from within a previously initialized Restart Table ++ * Allocate an index from within a previously initialized Restart Table. + */ + static inline void *alloc_rsttbl_idx(struct RESTART_TABLE **tbl) + { +@@ -900,7 +889,7 @@ static inline void *alloc_rsttbl_idx(struct RESTART_TABLE **tbl) + /* + * alloc_rsttbl_from_idx + * +- * allocates a specific index from within a previously initialized Restart Table ++ * Allocate a specific index from within a previously initialized Restart Table. + */ + static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) + { +@@ -910,23 +899,24 @@ static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) + u32 bytes = bytes_per_rt(rt); + u16 esize = le16_to_cpu(rt->size); + +- /* If the entry is not the table, we will have to extend the table */ ++ /* If the entry is not the table, we will have to extend the table. */ + if (vbo >= bytes) { + /* +- * extend the size by computing the number of entries between +- * the existing size and the desired index and adding +- * 1 to that ++ * Extend the size by computing the number of entries between ++ * the existing size and the desired index and adding 1 to that. + */ + u32 bytes2idx = vbo - bytes; + +- /* There should always be an integral number of entries being added */ +- /* Now extend the table */ ++ /* ++ * There should always be an integral number of entries ++ * being added. Now extend the table. ++ */ + *tbl = rt = extend_rsttbl(rt, bytes2idx / esize + 1, bytes); + if (!rt) + return NULL; + } + +- /* see if the entry is already allocated, and just return if it is. */ ++ /* See if the entry is already allocated, and just return if it is. */ + e = Add2Ptr(rt, vbo); + + if (*e == RESTART_ENTRY_ALLOCATED_LE) +@@ -934,7 +924,7 @@ static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) + + /* + * Walk through the table, looking for the entry we're +- * interested and the previous entry ++ * interested and the previous entry. + */ + off = le32_to_cpu(rt->first_free); + e = Add2Ptr(rt, off); +@@ -946,24 +936,28 @@ static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) + } + + /* +- * need to walk through the list looking for the predecessor of our entry ++ * Need to walk through the list looking for the predecessor ++ * of our entry. + */ + for (;;) { + /* Remember the entry just found */ + u32 last_off = off; + __le32 *last_e = e; + +- /* should never run of entries. */ ++ /* Should never run of entries. */ + +- /* Lookup up the next entry the list */ ++ /* Lookup up the next entry the list. */ + off = le32_to_cpu(*last_e); + e = Add2Ptr(rt, off); + +- /* If this is our match we are done */ ++ /* If this is our match we are done. */ + if (off == vbo) { + *last_e = *e; + +- /* If this was the last entry, we update that the table as well */ ++ /* ++ * If this was the last entry, we update that ++ * table as well. ++ */ + if (le32_to_cpu(rt->last_free) == off) + rt->last_free = cpu_to_le32(last_off); + break; +@@ -971,11 +965,11 @@ static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) + } + + skip_looking: +- /* If the list is now empty, we fix the last_free as well */ ++ /* If the list is now empty, we fix the last_free as well. */ + if (!rt->first_free) + rt->last_free = 0; + +- /* Zero this entry */ ++ /* Zero this entry. */ + memset(e, 0, esize); + *e = RESTART_ENTRY_ALLOCATED_LE; + +@@ -992,9 +986,7 @@ static inline void *alloc_rsttbl_from_idx(struct RESTART_TABLE **tbl, u32 vbo) + #define NTFSLOG_REUSE_TAIL 0x00000010 + #define NTFSLOG_NO_OLDEST_LSN 0x00000020 + +-/* +- * Helper struct to work with NTFS LogFile +- */ ++/* Helper struct to work with NTFS $LogFile. */ + struct ntfs_log { + struct ntfs_inode *ni; + +@@ -1022,15 +1014,15 @@ struct ntfs_log { + u32 file_data_bits; + u32 seq_num_mask; /* (1 << file_data_bits) - 1 */ + +- struct RESTART_AREA *ra; /* in-memory image of the next restart area */ +- u32 ra_size; /* the usable size of the restart area */ ++ struct RESTART_AREA *ra; /* In-memory image of the next restart area. */ ++ u32 ra_size; /* The usable size of the restart area. */ + + /* + * If true, then the in-memory restart area is to be written +- * to the first position on the disk ++ * to the first position on the disk. + */ + bool init_ra; +- bool set_dirty; /* true if we need to set dirty flag */ ++ bool set_dirty; /* True if we need to set dirty flag. */ + + u64 oldest_lsn; + +@@ -1048,7 +1040,7 @@ struct ntfs_log { + short minor_ver; + + u32 l_flags; /* See NTFSLOG_XXX */ +- u32 current_openlog_count; /* On-disk value for open_log_count */ ++ u32 current_openlog_count; /* On-disk value for open_log_count. */ + + struct CLIENT_ID client_id; + u32 client_undo_commit; +@@ -1061,7 +1053,7 @@ static inline u32 lsn_to_vbo(struct ntfs_log *log, const u64 lsn) + return vbo; + } + +-/* compute the offset in the log file of the next log page */ ++/* Compute the offset in the log file of the next log page. */ + static inline u32 next_page_off(struct ntfs_log *log, u32 off) + { + off = (off & ~log->sys_page_mask) + log->page_size; +@@ -1184,8 +1176,9 @@ static int read_log_page(struct ntfs_log *log, u32 vbo, + /* + * log_read_rst + * +- * it walks through 512 blocks of the file looking for a valid restart page header +- * It will stop the first time we find a valid page header ++ * It walks through 512 blocks of the file looking for a valid ++ * restart page header. It will stop the first time we find a ++ * valid page header. + */ + static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + struct restart_info *info) +@@ -1198,7 +1191,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + + memset(info, 0, sizeof(struct restart_info)); + +- /* Determine which restart area we are looking for */ ++ /* Determine which restart area we are looking for. */ + if (first) { + vbo = 0; + skip = 512; +@@ -1207,21 +1200,21 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + skip = 0; + } + +- /* loop continuously until we succeed */ ++ /* Loop continuously until we succeed. */ + for (; vbo < l_size; vbo = 2 * vbo + skip, skip = 0) { + bool usa_error; + u32 sys_page_size; + bool brst, bchk; + struct RESTART_AREA *ra; + +- /* Read a page header at the current offset */ ++ /* Read a page header at the current offset. */ + if (read_log_page(log, vbo, (struct RECORD_PAGE_HDR **)&r_page, + &usa_error)) { +- /* ignore any errors */ ++ /* Ignore any errors. */ + continue; + } + +- /* exit if the signature is a log record page */ ++ /* Exit if the signature is a log record page. */ + if (r_page->rhdr.sign == NTFS_RCRD_SIGNATURE) { + info->initialized = true; + break; +@@ -1234,7 +1227,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + if (r_page->rhdr.sign != NTFS_FFFF_SIGNATURE) { + /* + * Remember if the signature does not +- * indicate uninitialized file ++ * indicate uninitialized file. + */ + info->initialized = true; + } +@@ -1246,7 +1239,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + info->initialized = true; + info->vbo = vbo; + +- /* Let's check the restart area if this is a valid page */ ++ /* Let's check the restart area if this is a valid page. */ + if (!is_rst_page_hdr_valid(vbo, r_page)) + goto check_result; + ra = Add2Ptr(r_page, le16_to_cpu(r_page->ra_off)); +@@ -1257,14 +1250,14 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + /* + * We have a valid restart page header and restart area. + * If chkdsk was run or we have no clients then we have +- * no more checking to do ++ * no more checking to do. + */ + if (bchk || ra->client_idx[1] == LFS_NO_CLIENT_LE) { + info->valid_page = true; + goto check_result; + } + +- /* Read the entire restart area */ ++ /* Read the entire restart area. */ + sys_page_size = le32_to_cpu(r_page->sys_page_size); + if (DefaultLogPageSize != sys_page_size) { + kfree(r_page); +@@ -1275,7 +1268,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + if (read_log_page(log, vbo, + (struct RECORD_PAGE_HDR **)&r_page, + &usa_error)) { +- /* ignore any errors */ ++ /* Ignore any errors. */ + kfree(r_page); + r_page = NULL; + continue; +@@ -1288,7 +1281,10 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + } + + check_result: +- /* If chkdsk was run then update the caller's values and return */ ++ /* ++ * If chkdsk was run then update the caller's ++ * values and return. ++ */ + if (r_page->rhdr.sign == NTFS_CHKD_SIGNATURE) { + info->chkdsk_was_run = true; + info->last_lsn = le64_to_cpu(r_page->rhdr.lsn); +@@ -1297,7 +1293,10 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + return 0; + } + +- /* If we have a valid page then copy the values we need from it */ ++ /* ++ * If we have a valid page then copy the values ++ * we need from it. ++ */ + if (info->valid_page) { + info->last_lsn = le64_to_cpu(ra->current_lsn); + info->restart = true; +@@ -1312,9 +1311,7 @@ static int log_read_rst(struct ntfs_log *log, u32 l_size, bool first, + } + + /* +- * log_init_pg_hdr +- * +- * init "log' from restart page header ++ * Ilog_init_pg_hdr - Init @log from restart page header. + */ + static void log_init_pg_hdr(struct ntfs_log *log, u32 sys_page_size, + u32 page_size, u16 major_ver, u16 minor_ver) +@@ -1337,15 +1334,13 @@ static void log_init_pg_hdr(struct ntfs_log *log, u32 sys_page_size, + } + + /* +- * log_create +- * +- * init "log" in cases when we don't have a restart area to use ++ * log_create - Init @log in cases when we don't have a restart area to use. + */ + static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + u32 open_log_count, bool wrapped, bool use_multi_page) + { + log->l_size = l_size; +- /* All file offsets must be quadword aligned */ ++ /* All file offsets must be quadword aligned. */ + log->file_data_bits = blksize_bits(l_size) - 3; + log->seq_num_mask = (8 << log->file_data_bits) - 1; + log->seq_num_bits = sizeof(u64) * 8 - log->file_data_bits; +@@ -1357,21 +1352,21 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + + log->l_flags |= NTFSLOG_NO_LAST_LSN | NTFSLOG_NO_OLDEST_LSN; + +- /* Set the correct flags for the I/O and indicate if we have wrapped */ ++ /* Set the correct flags for the I/O and indicate if we have wrapped. */ + if (wrapped) + log->l_flags |= NTFSLOG_WRAPPED; + + if (use_multi_page) + log->l_flags |= NTFSLOG_MULTIPLE_PAGE_IO; + +- /* Compute the log page values */ ++ /* Compute the log page values. */ + log->data_off = ALIGN( + offsetof(struct RECORD_PAGE_HDR, fixups) + + sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1), 8); + log->data_size = log->page_size - log->data_off; + log->record_header_len = sizeof(struct LFS_RECORD_HDR); + +- /* Remember the different page sizes for reservation */ ++ /* Remember the different page sizes for reservation. */ + log->reserved = log->data_size - log->record_header_len; + + /* Compute the restart page values. */ +@@ -1384,15 +1379,15 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + + /* + * The total available log file space is the number of +- * log file pages times the space available on each page ++ * log file pages times the space available on each page. + */ + log->total_avail_pages = log->l_size - log->first_page; + log->total_avail = log->total_avail_pages >> log->page_bits; + + /* + * We assume that we can't use the end of the page less than +- * the file record size +- * Then we won't need to reserve more than the caller asks for ++ * the file record size. ++ * Then we won't need to reserve more than the caller asks for. + */ + log->max_current_avail = log->total_avail * log->reserved; + log->total_avail = log->total_avail * log->data_size; +@@ -1400,9 +1395,7 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + } + + /* +- * log_create_ra +- * +- * This routine is called to fill a restart area from the values stored in 'log' ++ * log_create_ra - Fill a restart area from the values stored in @log. + */ + static struct RESTART_AREA *log_create_ra(struct ntfs_log *log) + { +@@ -1442,12 +1435,12 @@ static u32 final_log_off(struct ntfs_log *log, u64 lsn, u32 data_len) + + page_off -= 1; + +- /* Add the length of the header */ ++ /* Add the length of the header. */ + data_len += log->record_header_len; + + /* +- * If this lsn is contained this log page we are done +- * Otherwise we need to walk through several log pages ++ * If this lsn is contained this log page we are done. ++ * Otherwise we need to walk through several log pages. + */ + if (data_len > tail) { + data_len -= tail; +@@ -1457,7 +1450,10 @@ static u32 final_log_off(struct ntfs_log *log, u64 lsn, u32 data_len) + for (;;) { + final_log_off = next_page_off(log, final_log_off); + +- /* We are done if the remaining bytes fit on this page */ ++ /* ++ * We are done if the remaining bytes ++ * fit on this page. ++ */ + if (data_len <= tail) + break; + data_len -= tail; +@@ -1466,7 +1462,7 @@ static u32 final_log_off(struct ntfs_log *log, u64 lsn, u32 data_len) + + /* + * We add the remaining bytes to our starting position on this page +- * and then add that value to the file offset of this log page ++ * and then add that value to the file offset of this log page. + */ + return final_log_off + data_len + page_off; + } +@@ -1483,11 +1479,11 @@ static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, + u64 seq = this_lsn >> log->file_data_bits; + struct RECORD_PAGE_HDR *page = NULL; + +- /* Remember if we wrapped */ ++ /* Remember if we wrapped. */ + if (end <= vbo) + seq += 1; + +- /* log page header for this page */ ++ /* Log page header for this page. */ + err = read_log_page(log, hdr_off, &page, NULL); + if (err) + return err; +@@ -1495,11 +1491,11 @@ static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, + /* + * If the lsn we were given was not the last lsn on this page, + * then the starting offset for the next lsn is on a quad word +- * boundary following the last file offset for the current lsn +- * Otherwise the file offset is the start of the data on the next page ++ * boundary following the last file offset for the current lsn. ++ * Otherwise the file offset is the start of the data on the next page. + */ + if (this_lsn == le64_to_cpu(page->rhdr.lsn)) { +- /* If we wrapped, we need to increment the sequence number */ ++ /* If we wrapped, we need to increment the sequence number. */ + hdr_off = next_page_off(log, hdr_off); + if (hdr_off == log->first_page) + seq += 1; +@@ -1509,12 +1505,12 @@ static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, + vbo = ALIGN(end, 8); + } + +- /* Compute the lsn based on the file offset and the sequence count */ ++ /* Compute the lsn based on the file offset and the sequence count. */ + *lsn = vbo_to_lsn(log, vbo, seq); + + /* +- * If this lsn is within the legal range for the file, we return true +- * Otherwise false indicates that there are no more lsn's ++ * If this lsn is within the legal range for the file, we return true. ++ * Otherwise false indicates that there are no more lsn's. + */ + if (!is_lsn_in_file(log, *lsn)) + *lsn = 0; +@@ -1525,32 +1521,30 @@ static int next_log_lsn(struct ntfs_log *log, const struct LFS_RECORD_HDR *rh, + } + + /* +- * current_log_avail +- * +- * calculate the number of bytes available for log records ++ * current_log_avail - Calculate the number of bytes available for log records. + */ + static u32 current_log_avail(struct ntfs_log *log) + { + u32 oldest_off, next_free_off, free_bytes; + + if (log->l_flags & NTFSLOG_NO_LAST_LSN) { +- /* The entire file is available */ ++ /* The entire file is available. */ + return log->max_current_avail; + } + + /* + * If there is a last lsn the restart area then we know that we will +- * have to compute the free range +- * If there is no oldest lsn then start at the first page of the file ++ * have to compute the free range. ++ * If there is no oldest lsn then start at the first page of the file. + */ + oldest_off = (log->l_flags & NTFSLOG_NO_OLDEST_LSN) + ? log->first_page + : (log->oldest_lsn_off & ~log->sys_page_mask); + + /* +- * We will use the next log page offset to compute the next free page\ +- * If we are going to reuse this page go to the next page +- * If we are at the first page then use the end of the file ++ * We will use the next log page offset to compute the next free page. ++ * If we are going to reuse this page go to the next page. ++ * If we are at the first page then use the end of the file. + */ + next_free_off = (log->l_flags & NTFSLOG_REUSE_TAIL) + ? log->next_page + log->page_size +@@ -1558,12 +1552,12 @@ static u32 current_log_avail(struct ntfs_log *log) + ? log->l_size + : log->next_page; + +- /* If the two offsets are the same then there is no available space */ ++ /* If the two offsets are the same then there is no available space. */ + if (oldest_off == next_free_off) + return 0; + /* + * If the free offset follows the oldest offset then subtract +- * this range from the total available pages ++ * this range from the total available pages. + */ + free_bytes = + oldest_off < next_free_off +@@ -1587,13 +1581,13 @@ static bool check_subseq_log_page(struct ntfs_log *log, + + /* + * If the last lsn on the page occurs was written after the page +- * that caused the original error then we have a fatal error ++ * that caused the original error then we have a fatal error. + */ + lsn_seq = lsn >> log->file_data_bits; + + /* + * If the sequence number for the lsn the page is equal or greater +- * than lsn we expect, then this is a subsequent write ++ * than lsn we expect, then this is a subsequent write. + */ + return lsn_seq >= seq || + (lsn_seq == seq - 1 && log->first_page == vbo && +@@ -1603,8 +1597,8 @@ static bool check_subseq_log_page(struct ntfs_log *log, + /* + * last_log_lsn + * +- * This routine walks through the log pages for a file, searching for the +- * last log page written to the file ++ * Walks through the log pages for a file, searching for the ++ * last log page written to the file. + */ + static int last_log_lsn(struct ntfs_log *log) + { +@@ -1653,7 +1647,7 @@ static int last_log_lsn(struct ntfs_log *log) + } + + next_tail: +- /* Read second tail page (at pos 3/0x12000) */ ++ /* Read second tail page (at pos 3/0x12000). */ + if (read_log_page(log, second_off, &second_tail, &usa_error) || + usa_error || second_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) { + kfree(second_tail); +@@ -1665,7 +1659,7 @@ static int last_log_lsn(struct ntfs_log *log) + lsn2 = le64_to_cpu(second_tail->record_hdr.last_end_lsn); + } + +- /* Read first tail page (at pos 2/0x2000 ) */ ++ /* Read first tail page (at pos 2/0x2000). */ + if (read_log_page(log, final_off, &first_tail, &usa_error) || + usa_error || first_tail->rhdr.sign != NTFS_RCRD_SIGNATURE) { + kfree(first_tail); +@@ -1811,10 +1805,10 @@ static int last_log_lsn(struct ntfs_log *log) + + next_page: + tail_page = NULL; +- /* Read the next log page */ ++ /* Read the next log page. */ + err = read_log_page(log, curpage_off, &page, &usa_error); + +- /* Compute the next log page offset the file */ ++ /* Compute the next log page offset the file. */ + nextpage_off = next_page_off(log, curpage_off); + wrapped = nextpage_off == log->first_page; + +@@ -1893,14 +1887,14 @@ static int last_log_lsn(struct ntfs_log *log) + + /* + * If we are at the expected first page of a transfer check to see +- * if either tail copy is at this offset ++ * if either tail copy is at this offset. + * If this page is the last page of a transfer, check if we wrote +- * a subsequent tail copy ++ * a subsequent tail copy. + */ + if (page_cnt == page_pos || page_cnt == page_pos + 1) { + /* + * Check if the offset matches either the first or second +- * tail copy. It is possible it will match both ++ * tail copy. It is possible it will match both. + */ + if (curpage_off == final_off) + tail_page = first_tail; +@@ -1922,32 +1916,35 @@ static int last_log_lsn(struct ntfs_log *log) + + use_tail_page: + if (tail_page) { +- /* we have a candidate for a tail copy */ ++ /* We have a candidate for a tail copy. */ + lsn_cur = le64_to_cpu(tail_page->record_hdr.last_end_lsn); + + if (last_ok_lsn < lsn_cur) { + /* + * If the sequence number is not expected, +- * then don't use the tail copy ++ * then don't use the tail copy. + */ + if (expected_seq != (lsn_cur >> log->file_data_bits)) + tail_page = NULL; + } else if (last_ok_lsn > lsn_cur) { + /* + * If the last lsn is greater than the one on +- * this page then forget this tail ++ * this page then forget this tail. + */ + tail_page = NULL; + } + } + +- /* If we have an error on the current page, we will break of this loop */ ++ /* ++ *If we have an error on the current page, ++ * we will break of this loop. ++ */ + if (err || usa_error) + goto check_tail; + + /* + * Done if the last lsn on this page doesn't match the previous known +- * last lsn or the sequence number is not expected ++ * last lsn or the sequence number is not expected. + */ + lsn_cur = le64_to_cpu(page->rhdr.lsn); + if (last_ok_lsn != lsn_cur && +@@ -1956,9 +1953,9 @@ static int last_log_lsn(struct ntfs_log *log) + } + + /* +- * Check that the page position and page count values are correct ++ * Check that the page position and page count values are correct. + * If this is the first page of a transfer the position must be 1 +- * and the count will be unknown ++ * and the count will be unknown. + */ + if (page_cnt == page_pos) { + if (page->page_pos != cpu_to_le16(1) && +@@ -1975,20 +1972,20 @@ static int last_log_lsn(struct ntfs_log *log) + le16_to_cpu(page->page_pos) != page_pos + 1) { + /* + * The page position better be 1 more than the last page +- * position and the page count better match ++ * position and the page count better match. + */ + goto check_tail; + } + + /* + * We have a valid page the file and may have a valid page +- * the tail copy area ++ * the tail copy area. + * If the tail page was written after the page the file then +- * break of the loop ++ * break of the loop. + */ + if (tail_page && + le64_to_cpu(tail_page->record_hdr.last_end_lsn) > lsn_cur) { +- /* Remember if we will replace the page */ ++ /* Remember if we will replace the page. */ + replace_page = true; + goto check_tail; + } +@@ -1998,7 +1995,7 @@ static int last_log_lsn(struct ntfs_log *log) + if (is_log_record_end(page)) { + /* + * Since we have read this page we know the sequence number +- * is the same as our expected value ++ * is the same as our expected value. + */ + log->seq_num = expected_seq; + log->last_lsn = le64_to_cpu(page->record_hdr.last_end_lsn); +@@ -2007,7 +2004,7 @@ static int last_log_lsn(struct ntfs_log *log) + + /* + * If there is room on this page for another header then +- * remember we want to reuse the page ++ * remember we want to reuse the page. + */ + if (log->record_header_len <= + log->page_size - +@@ -2019,14 +2016,14 @@ static int last_log_lsn(struct ntfs_log *log) + log->next_page = nextpage_off; + } + +- /* Remember if we wrapped the log file */ ++ /* Remember if we wrapped the log file. */ + if (wrapped_file) + log->l_flags |= NTFSLOG_WRAPPED; + } + + /* + * Remember the last page count and position. +- * Also remember the last known lsn ++ * Also remember the last known lsn. + */ + page_cnt = le16_to_cpu(page->page_count); + page_pos = le16_to_cpu(page->page_pos); +@@ -2067,19 +2064,19 @@ static int last_log_lsn(struct ntfs_log *log) + log->l_flags |= NTFSLOG_WRAPPED; + } + +- /* Remember that the partial IO will start at the next page */ ++ /* Remember that the partial IO will start at the next page. */ + second_off = nextpage_off; + + /* + * If the next page is the first page of the file then update +- * the sequence number for log records which begon the next page ++ * the sequence number for log records which begon the next page. + */ + if (wrapped) + expected_seq += 1; + + /* + * If we have a tail copy or are performing single page I/O we can +- * immediately look at the next page ++ * immediately look at the next page. + */ + if (replace_page || (log->ra->flags & RESTART_SINGLE_PAGE_IO)) { + page_cnt = 2; +@@ -2105,19 +2102,19 @@ static int last_log_lsn(struct ntfs_log *log) + kfree(tst_page); + tst_page = NULL; + +- /* Walk through the file, reading log pages */ ++ /* Walk through the file, reading log pages. */ + err = read_log_page(log, nextpage_off, &tst_page, &usa_error); + + /* + * If we get a USA error then assume that we correctly found +- * the end of the original transfer ++ * the end of the original transfer. + */ + if (usa_error) + goto file_is_valid; + + /* + * If we were able to read the page, we examine it to see if it +- * is the same or different Io block ++ * is the same or different Io block. + */ + if (err) + goto next_test_page_1; +@@ -2148,7 +2145,7 @@ static int last_log_lsn(struct ntfs_log *log) + goto next_test_page; + + check_valid: +- /* Skip over the remaining pages this transfer */ ++ /* Skip over the remaining pages this transfer. */ + remain_pages = page_cnt - page_pos - 1; + part_io_count += remain_pages; + +@@ -2160,7 +2157,7 @@ static int last_log_lsn(struct ntfs_log *log) + expected_seq += 1; + } + +- /* Call our routine to check this log page */ ++ /* Call our routine to check this log page. */ + kfree(tst_page); + tst_page = NULL; + +@@ -2173,7 +2170,7 @@ static int last_log_lsn(struct ntfs_log *log) + + file_is_valid: + +- /* We have a valid file */ ++ /* We have a valid file. */ + if (page_off1 || tail_page) { + struct RECORD_PAGE_HDR *tmp_page; + +@@ -2203,11 +2200,11 @@ static int last_log_lsn(struct ntfs_log *log) + + /* + * Correct page and copy the data from this page +- * into it and flush it to disk ++ * into it and flush it to disk. + */ + memcpy(page, tmp_page, log->page_size); + +- /* Fill last flushed lsn value flush the page */ ++ /* Fill last flushed lsn value flush the page. */ + if (log->major_ver < 2) + page->rhdr.lsn = page->record_hdr.last_end_lsn; + else +@@ -2251,10 +2248,9 @@ static int last_log_lsn(struct ntfs_log *log) + } + + /* +- * read_log_rec_buf ++ * read_log_rec_buf - Copy a log record from the file to a buffer. + * +- * copies a log record from the file to a buffer +- * The log record may span several log pages and may even wrap the file ++ * The log record may span several log pages and may even wrap the file. + */ + static int read_log_rec_buf(struct ntfs_log *log, + const struct LFS_RECORD_HDR *rh, void *buffer) +@@ -2268,7 +2264,7 @@ static int read_log_rec_buf(struct ntfs_log *log, + + /* + * While there are more bytes to transfer, +- * we continue to attempt to perform the read ++ * we continue to attempt to perform the read. + */ + for (;;) { + bool usa_error; +@@ -2285,7 +2281,7 @@ static int read_log_rec_buf(struct ntfs_log *log, + + /* + * The last lsn on this page better be greater or equal +- * to the lsn we are copying ++ * to the lsn we are copying. + */ + if (lsn > le64_to_cpu(ph->rhdr.lsn)) { + err = -EINVAL; +@@ -2294,7 +2290,7 @@ static int read_log_rec_buf(struct ntfs_log *log, + + memcpy(buffer, Add2Ptr(ph, off), tail); + +- /* If there are no more bytes to transfer, we exit the loop */ ++ /* If there are no more bytes to transfer, we exit the loop. */ + if (!data_len) { + if (!is_log_record_end(ph) || + lsn > le64_to_cpu(ph->record_hdr.last_end_lsn)) { +@@ -2314,8 +2310,8 @@ static int read_log_rec_buf(struct ntfs_log *log, + off = log->data_off; + + /* +- * adjust our pointer the user's buffer to transfer +- * the next block to ++ * Adjust our pointer the user's buffer to transfer ++ * the next block to. + */ + buffer = Add2Ptr(buffer, tail); + } +@@ -2339,7 +2335,7 @@ static int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_, + *lsn = 0; + *rst_ = NULL; + +- /* If the client doesn't have a restart area, go ahead and exit now */ ++ /* If the client doesn't have a restart area, go ahead and exit now. */ + if (!lsnc) + return 0; + +@@ -2352,7 +2348,7 @@ static int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_, + lsnr = le64_to_cpu(rh->this_lsn); + + if (lsnc != lsnr) { +- /* If the lsn values don't match, then the disk is corrupt */ ++ /* If the lsn values don't match, then the disk is corrupt. */ + err = -EINVAL; + goto out; + } +@@ -2376,7 +2372,7 @@ static int read_rst_area(struct ntfs_log *log, struct NTFS_RESTART **rst_, + goto out; + } + +- /* Copy the data into the 'rst' buffer */ ++ /* Copy the data into the 'rst' buffer. */ + err = read_log_rec_buf(log, rh, rst); + if (err) + goto out; +@@ -2397,7 +2393,7 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + struct LFS_RECORD_HDR *rh = lcb->lrh; + u32 rec_len, len; + +- /* Read the record header for this lsn */ ++ /* Read the record header for this lsn. */ + if (!rh) { + err = read_log_page(log, lsn_to_vbo(log, lsn), + (struct RECORD_PAGE_HDR **)&rh, NULL); +@@ -2409,7 +2405,7 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + + /* + * If the lsn the log record doesn't match the desired +- * lsn then the disk is corrupt ++ * lsn then the disk is corrupt. + */ + if (lsn != le64_to_cpu(rh->this_lsn)) + return -EINVAL; +@@ -2417,8 +2413,8 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + len = le32_to_cpu(rh->client_data_len); + + /* +- * check that the length field isn't greater than the total +- * available space the log file ++ * Check that the length field isn't greater than the total ++ * available space the log file. + */ + rec_len = len + log->record_header_len; + if (rec_len >= log->total_avail) +@@ -2426,7 +2422,7 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + + /* + * If the entire log record is on this log page, +- * put a pointer to the log record the context block ++ * put a pointer to the log record the context block. + */ + if (rh->flags & LOG_RECORD_MULTI_PAGE) { + void *lr = kmalloc(len, GFP_NOFS); +@@ -2437,12 +2433,12 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + lcb->log_rec = lr; + lcb->alloc = true; + +- /* Copy the data into the buffer returned */ ++ /* Copy the data into the buffer returned. */ + err = read_log_rec_buf(log, rh, lr); + if (err) + return err; + } else { +- /* If beyond the end of the current page -> an error */ ++ /* If beyond the end of the current page -> an error. */ + u32 page_off = lsn_to_page_off(log, lsn); + + if (page_off + len + log->record_header_len > log->page_size) +@@ -2456,9 +2452,7 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb) + } + + /* +- * read_log_rec_lcb +- * +- * initiates the query operation. ++ * read_log_rec_lcb - Init the query operation. + */ + static int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode, + struct lcb **lcb_) +@@ -2476,7 +2470,7 @@ static int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode, + return -EINVAL; + } + +- /* check that the given lsn is the legal range for this client */ ++ /* Check that the given lsn is the legal range for this client. */ + cr = Add2Ptr(log->ra, le16_to_cpu(log->ra->client_off)); + + if (!verify_client_lsn(log, cr, lsn)) +@@ -2488,7 +2482,7 @@ static int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode, + lcb->client = log->client_id; + lcb->ctx_mode = ctx_mode; + +- /* Find the log record indicated by the given lsn */ ++ /* Find the log record indicated by the given lsn. */ + err = find_log_rec(log, lsn, lcb); + if (err) + goto out; +@@ -2505,7 +2499,7 @@ static int read_log_rec_lcb(struct ntfs_log *log, u64 lsn, u32 ctx_mode, + /* + * find_client_next_lsn + * +- * attempt to find the next lsn to return to a client based on the context mode. ++ * Attempt to find the next lsn to return to a client based on the context mode. + */ + static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + { +@@ -2519,7 +2513,7 @@ static int find_client_next_lsn(struct ntfs_log *log, struct lcb *lcb, u64 *lsn) + if (lcb_ctx_next != lcb->ctx_mode) + goto check_undo_next; + +- /* Loop as long as another lsn can be found */ ++ /* Loop as long as another lsn can be found. */ + for (;;) { + u64 current_lsn; + +@@ -2705,7 +2699,7 @@ static inline bool check_attr(const struct MFT_REC *rec, + u64 dsize, svcn, evcn; + u16 run_off; + +- /* Check the fixed part of the attribute record header */ ++ /* Check the fixed part of the attribute record header. */ + if (asize >= sbi->record_size || + asize + PtrOffset(rec, attr) >= sbi->record_size || + (attr->name_len && +@@ -2714,7 +2708,7 @@ static inline bool check_attr(const struct MFT_REC *rec, + return false; + } + +- /* Check the attribute fields */ ++ /* Check the attribute fields. */ + switch (attr->non_res) { + case 0: + rsize = le32_to_cpu(attr->res.data_size); +@@ -2797,7 +2791,7 @@ static inline bool check_file_record(const struct MFT_REC *rec, + u16 ao = le16_to_cpu(rec->attr_off); + u32 rs = sbi->record_size; + +- /* check the file record header for consistency */ ++ /* Check the file record header for consistency. */ + if (rec->rhdr.sign != NTFS_FILE_SIGNATURE || + fo > (SECTOR_SIZE - ((rs >> SECTOR_SHIFT) + 1) * sizeof(short)) || + (fn - 1) * SECTOR_SIZE != rs || ao < MFTRECORD_FIXUP_OFFSET_1 || +@@ -2806,7 +2800,7 @@ static inline bool check_file_record(const struct MFT_REC *rec, + return false; + } + +- /* Loop to check all of the attributes */ ++ /* Loop to check all of the attributes. */ + for (attr = Add2Ptr(rec, ao); attr->type != ATTR_END; + attr = Add2Ptr(attr, le32_to_cpu(attr->size))) { + if (check_attr(rec, attr, sbi)) +@@ -2959,7 +2953,11 @@ struct OpenAttr { + // CLST rno; + }; + +-/* Returns 0 if 'attr' has the same type and name */ ++/* ++ * cmp_type_and_name ++ * ++ * Return: 0 if 'attr' has the same type and name. ++ */ + static inline int cmp_type_and_name(const struct ATTRIB *a1, + const struct ATTRIB *a2) + { +@@ -3032,10 +3030,8 @@ static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi, + } + + /* +- * do_action +- * +- * common routine for the Redo and Undo Passes +- * If rlsn is NULL then undo ++ * do_action - Common routine for the Redo and Undo Passes. ++ * @rlsn: If it is NULL then undo. + */ + static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + const struct LOG_REC_HDR *lrh, u32 op, void *data, +@@ -3072,10 +3068,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + + oa = oe->ptr; + +- /* Big switch to prepare */ ++ /* Big switch to prepare. */ + switch (op) { + /* ============================================================ +- * Process MFT records, as described by the current log record ++ * Process MFT records, as described by the current log record. + * ============================================================ + */ + case InitializeFileRecordSegment: +@@ -3104,7 +3100,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + if (err) + goto out; + } else { +- /* read from disk */ ++ /* Read from disk. */ + err = mi_get(sbi, rno, &mi); + if (err) + return err; +@@ -3161,9 +3157,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + inode_parent = NULL; + break; + +- /* ============================================================ +- * Process attributes, as described by the current log record +- * ============================================================ ++ /* ++ * Process attributes, as described by the current log record. + */ + case UpdateNonresidentValue: + case AddIndexEntryAllocation: +@@ -3208,7 +3203,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + WARN_ON(1); + } + +- /* Big switch to do operation */ ++ /* Big switch to do operation. */ + switch (op) { + case InitializeFileRecordSegment: + if (roff + dlen > record_size) +@@ -3311,7 +3306,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + + if (nsize < asize) { + memmove(Add2Ptr(attr, aoff), data, dlen); +- data = NULL; // To skip below memmove ++ data = NULL; // To skip below memmove(). + } + + memmove(Add2Ptr(attr, nsize), Add2Ptr(attr, asize), +@@ -3734,11 +3729,11 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + } + + /* +- * log_replay ++ * log_replay - Replays log and empties it. + * +- * this function is called during mount operation +- * it replays log and empties it +- * initialized is set false if logfile contains '-1' ++ * This function is called during mount operation. ++ * It replays log and empties it. ++ * Initialized is set false if logfile contains '-1'. + */ + int log_replay(struct ntfs_inode *ni, bool *initialized) + { +@@ -3784,7 +3779,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + u16 t16; + u32 t32; + +- /* Get the size of page. NOTE: To replay we can use default page */ ++ /* Get the size of page. NOTE: To replay we can use default page. */ + #if PAGE_SIZE >= DefaultLogPageSize && PAGE_SIZE <= DefaultLogPageSize * 2 + page_size = norm_file_page(PAGE_SIZE, &l_size, true); + #else +@@ -3810,7 +3805,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + log->page_mask = page_size - 1; + log->page_bits = blksize_bits(page_size); + +- /* Look for a restart area on the disk */ ++ /* Look for a restart area on the disk. */ + err = log_read_rst(log, l_size, true, &rst_info); + if (err) + goto out; +@@ -3820,7 +3815,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + if (!rst_info.restart) { + if (rst_info.initialized) { +- /* no restart area but the file is not initialized */ ++ /* No restart area but the file is not initialized. */ + err = -EINVAL; + goto out; + } +@@ -3843,14 +3838,14 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * If the restart offset above wasn't zero then we won't +- * look for a second restart ++ * look for a second restart. + */ + if (rst_info.vbo) + goto check_restart_area; + + err = log_read_rst(log, l_size, false, &rst_info2); + +- /* Determine which restart area to use */ ++ /* Determine which restart area to use. */ + if (!rst_info2.restart || rst_info2.last_lsn <= rst_info.last_lsn) + goto use_first_page; + +@@ -3877,10 +3872,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + kfree(rst_info2.r_page); + + check_restart_area: +- /* If the restart area is at offset 0, we want to write the second restart area first */ ++ /* ++ * If the restart area is at offset 0, we want ++ * to write the second restart area first. ++ */ + log->init_ra = !!rst_info.vbo; + +- /* If we have a valid page then grab a pointer to the restart area */ ++ /* If we have a valid page then grab a pointer to the restart area. */ + ra2 = rst_info.valid_page + ? Add2Ptr(rst_info.r_page, + le16_to_cpu(rst_info.r_page->ra_off)) +@@ -3892,7 +3890,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + bool use_multi_page = false; + u32 open_log_count; + +- /* Do some checks based on whether we have a valid log page */ ++ /* Do some checks based on whether we have a valid log page. */ + if (!rst_info.valid_page) { + open_log_count = get_random_int(); + goto init_log_instance; +@@ -3901,7 +3899,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * If the restart page size isn't changing then we want to +- * check how much work we need to do ++ * check how much work we need to do. + */ + if (page_size != le32_to_cpu(rst_info.r_page->sys_page_size)) + goto init_log_instance; +@@ -3919,7 +3917,9 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + } + log->ra = ra; + +- /* Put the restart areas and initialize the log file as required */ ++ /* Put the restart areas and initialize ++ * the log file as required. ++ */ + goto process_log; + } + +@@ -3929,9 +3929,9 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + } + + /* +- * If the log page or the system page sizes have changed, we can't use the log file +- * We must use the system page size instead of the default size +- * if there is not a clean shutdown ++ * If the log page or the system page sizes have changed, we can't ++ * use the log file. We must use the system page size instead of the ++ * default size if there is not a clean shutdown. + */ + t32 = le32_to_cpu(rst_info.r_page->sys_page_size); + if (page_size != t32) { +@@ -3946,7 +3946,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- /* If the file size has shrunk then we won't mount it */ ++ /* If the file size has shrunk then we won't mount it. */ + if (l_size < le64_to_cpu(ra2->l_size)) { + err = -EINVAL; + goto out; +@@ -3973,27 +3973,30 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + vbo = lsn_to_vbo(log, log->last_lsn); + + if (vbo < log->first_page) { +- /* This is a pseudo lsn */ ++ /* This is a pseudo lsn. */ + log->l_flags |= NTFSLOG_NO_LAST_LSN; + log->next_page = log->first_page; + goto find_oldest; + } + +- /* Find the end of this log record */ ++ /* Find the end of this log record. */ + off = final_log_off(log, log->last_lsn, + le32_to_cpu(ra2->last_lsn_data_len)); + +- /* If we wrapped the file then increment the sequence number */ ++ /* If we wrapped the file then increment the sequence number. */ + if (off <= vbo) { + log->seq_num += 1; + log->l_flags |= NTFSLOG_WRAPPED; + } + +- /* Now compute the next log page to use */ ++ /* Now compute the next log page to use. */ + vbo &= ~log->sys_page_mask; + tail = log->page_size - (off & log->page_mask) - 1; + +- /* If we can fit another log record on the page, move back a page the log file */ ++ /* ++ *If we can fit another log record on the page, ++ * move back a page the log file. ++ */ + if (tail >= log->record_header_len) { + log->l_flags |= NTFSLOG_REUSE_TAIL; + log->next_page = vbo; +@@ -4002,7 +4005,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + } + + find_oldest: +- /* Find the oldest client lsn. Use the last flushed lsn as a starting point */ ++ /* ++ * Find the oldest client lsn. Use the last ++ * flushed lsn as a starting point. ++ */ + log->oldest_lsn = log->last_lsn; + oldest_client_lsn(Add2Ptr(ra2, le16_to_cpu(ra2->client_off)), + ra2->client_idx[1], &log->oldest_lsn); +@@ -4048,18 +4054,18 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + le32_add_cpu(&ra->open_log_count, 1); + +- /* Now we need to walk through looking for the last lsn */ ++ /* Now we need to walk through looking for the last lsn. */ + err = last_log_lsn(log); + if (err) + goto out; + + log->current_avail = current_log_avail(log); + +- /* Remember which restart area to write first */ ++ /* Remember which restart area to write first. */ + log->init_ra = rst_info.vbo; + + process_log: +- /* 1.0, 1.1, 2.0 log->major_ver/minor_ver - short values */ ++ /* 1.0, 1.1, 2.0 log->major_ver/minor_ver - short values. */ + switch ((log->major_ver << 16) + log->minor_ver) { + case 0x10000: + case 0x10001: +@@ -4073,12 +4079,12 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- /* One client "NTFS" per logfile */ ++ /* One client "NTFS" per logfile. */ + ca = Add2Ptr(ra, le16_to_cpu(ra->client_off)); + + for (client = ra->client_idx[1];; client = cr->next_client) { + if (client == LFS_NO_CLIENT_LE) { +- /* Insert "NTFS" client LogFile */ ++ /* Insert "NTFS" client LogFile. */ + client = ra->client_idx[0]; + if (client == LFS_NO_CLIENT_LE) + return -EINVAL; +@@ -4110,7 +4116,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + break; + } + +- /* Update the client handle with the client block information */ ++ /* Update the client handle with the client block information. */ + log->client_id.seq_num = cr->seq_num; + log->client_id.client_idx = client; + +@@ -4127,7 +4133,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (!checkpt_lsn) + checkpt_lsn = ra_lsn; + +- /* Allocate and Read the Transaction Table */ ++ /* Allocate and Read the Transaction Table. */ + if (!rst->transact_table_len) + goto check_dirty_page_table; + +@@ -4151,7 +4157,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + rt = Add2Ptr(lrh, t16); + t32 = rec_len - t16; + +- /* Now check that this is a valid restart table */ ++ /* Now check that this is a valid restart table. */ + if (!check_rstbl(rt, t32)) { + err = -EINVAL; + goto out; +@@ -4167,7 +4173,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + lcb = NULL; + + check_dirty_page_table: +- /* The next record back should be the Dirty Pages Table */ ++ /* The next record back should be the Dirty Pages Table. */ + if (!rst->dirty_pages_len) + goto check_attribute_names; + +@@ -4191,7 +4197,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + rt = Add2Ptr(lrh, t16); + t32 = rec_len - t16; + +- /* Now check that this is a valid restart table */ ++ /* Now check that this is a valid restart table. */ + if (!check_rstbl(rt, t32)) { + err = -EINVAL; + goto out; +@@ -4203,14 +4209,14 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- /* Convert Ra version '0' into version '1' */ ++ /* Convert Ra version '0' into version '1'. */ + if (rst->major_ver) + goto end_conv_1; + + dp = NULL; + while ((dp = enum_rstbl(dptbl, dp))) { + struct DIR_PAGE_ENTRY_32 *dp0 = (struct DIR_PAGE_ENTRY_32 *)dp; +- // NOTE: Danger. Check for of boundary ++ // NOTE: Danger. Check for of boundary. + memmove(&dp->vcn, &dp0->vcn_low, + 2 * sizeof(u64) + + le32_to_cpu(dp->lcns_follow) * sizeof(u64)); +@@ -4220,7 +4226,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + lcb_put(lcb); + lcb = NULL; + +- /* Go through the table and remove the duplicates, remembering the oldest lsn values */ ++ /* ++ * Go through the table and remove the duplicates, ++ * remembering the oldest lsn values. ++ */ + if (sbi->cluster_size <= log->page_size) + goto trace_dp_table; + +@@ -4242,7 +4251,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + } + trace_dp_table: + check_attribute_names: +- /* The next record should be the Attribute Names */ ++ /* The next record should be the Attribute Names. */ + if (!rst->attr_names_len) + goto check_attr_table; + +@@ -4270,7 +4279,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + lcb = NULL; + + check_attr_table: +- /* The next record should be the attribute Table */ ++ /* The next record should be the attribute Table. */ + if (!rst->open_attr_len) + goto check_attribute_names2; + +@@ -4307,13 +4316,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + log->open_attr_tbl = oatbl; + +- /* Clear all of the Attr pointers */ ++ /* Clear all of the Attr pointers. */ + oe = NULL; + while ((oe = enum_rstbl(oatbl, oe))) { + if (!rst->major_ver) { + struct OPEN_ATTR_ENRTY_32 oe0; + +- /* Really 'oe' points to OPEN_ATTR_ENRTY_32 */ ++ /* Really 'oe' points to OPEN_ATTR_ENRTY_32. */ + memcpy(&oe0, oe, SIZEOF_OPENATTRIBUTEENTRY0); + + oe->bytes_per_index = oe0.bytes_per_index; +@@ -4351,7 +4360,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + trace_attribute_table: + /* + * If the checkpt_lsn is zero, then this is a freshly +- * formatted disk and we have no work to do ++ * formatted disk and we have no work to do. + */ + if (!checkpt_lsn) { + err = 0; +@@ -4371,12 +4380,12 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + /* Start the analysis pass from the Checkpoint lsn. */ + rec_lsn = checkpt_lsn; + +- /* Read the first lsn */ ++ /* Read the first lsn. */ + err = read_log_rec_lcb(log, checkpt_lsn, lcb_ctx_next, &lcb); + if (err) + goto out; + +- /* Loop to read all subsequent records to the end of the log file */ ++ /* Loop to read all subsequent records to the end of the log file. */ + next_log_record_analyze: + err = read_next_log_rec(log, lcb, &rec_lsn); + if (err) +@@ -4397,7 +4406,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * The first lsn after the previous lsn remembered +- * the checkpoint is the first candidate for the rlsn ++ * the checkpoint is the first candidate for the rlsn. + */ + if (!rlsn) + rlsn = rec_lsn; +@@ -4406,8 +4415,8 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto next_log_record_analyze; + + /* +- * Now update the Transaction Table for this transaction +- * If there is no entry present or it is unallocated we allocate the entry ++ * Now update the Transaction Table for this transaction. If there ++ * is no entry present or it is unallocated we allocate the entry. + */ + if (!trtbl) { + trtbl = init_rsttbl(sizeof(struct TRANSACTION_ENTRY), +@@ -4435,12 +4444,12 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * If this is a compensation log record, then change +- * the undo_next_lsn to be the undo_next_lsn of this record ++ * the undo_next_lsn to be the undo_next_lsn of this record. + */ + if (lrh->undo_op == cpu_to_le16(CompensationLogRecord)) + tr->undo_next_lsn = frh->client_undo_next_lsn; + +- /* Dispatch to handle log record depending on type */ ++ /* Dispatch to handle log record depending on type. */ + switch (le16_to_cpu(lrh->redo_op)) { + case InitializeFileRecordSegment: + case DeallocateFileRecordSegment: +@@ -4474,7 +4483,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * Calculate the number of clusters per page the system +- * which wrote the checkpoint, possibly creating the table ++ * which wrote the checkpoint, possibly creating the table. + */ + if (dptbl) { + t32 = (le16_to_cpu(dptbl->size) - +@@ -4504,9 +4513,9 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + copy_lcns: + /* +- * Copy the Lcns from the log record into the Dirty Page Entry +- * TODO: for different page size support, must somehow make +- * whole routine a loop, case Lcns do not fit below ++ * Copy the Lcns from the log record into the Dirty Page Entry. ++ * TODO: For different page size support, must somehow make ++ * whole routine a loop, case Lcns do not fit below. + */ + t16 = le16_to_cpu(lrh->lcns_follow); + for (i = 0; i < t16; i++) { +@@ -4523,7 +4532,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + const struct LCN_RANGE *r = + Add2Ptr(lrh, le16_to_cpu(lrh->redo_off)); + +- /* Loop through all of the Lcn ranges this log record */ ++ /* Loop through all of the Lcn ranges this log record. */ + for (i = 0; i < range_count; i++, r++) { + u64 lcn0 = le64_to_cpu(r->lcn); + u64 lcn_e = lcn0 + le64_to_cpu(r->len) - 1; +@@ -4549,7 +4558,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (t16 >= bytes_per_rt(oatbl)) { + /* + * Compute how big the table needs to be. +- * Add 10 extra entries for some cushion ++ * Add 10 extra entries for some cushion. + */ + u32 new_e = t16 / le16_to_cpu(oatbl->size); + +@@ -4563,7 +4572,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + } + } + +- /* Point to the entry being opened */ ++ /* Point to the entry being opened. */ + oe = alloc_rsttbl_from_idx(&oatbl, t16); + log->open_attr_tbl = oatbl; + if (!oe) { +@@ -4571,10 +4580,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- /* Initialize this entry from the log record */ ++ /* Initialize this entry from the log record. */ + t16 = le16_to_cpu(lrh->redo_off); + if (!rst->major_ver) { +- /* Convert version '0' into version '1' */ ++ /* Convert version '0' into version '1'. */ + struct OPEN_ATTR_ENRTY_32 *oe0 = Add2Ptr(lrh, t16); + + oe->bytes_per_index = oe0->bytes_per_index; +@@ -4642,13 +4651,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + case AttributeNamesDump: + case DirtyPageTableDump: + case TransactionTableDump: +- /* The following cases require no action the Analysis Pass */ ++ /* The following cases require no action the Analysis Pass. */ + goto next_log_record_analyze; + + default: + /* + * All codes will be explicitly handled. +- * If we see a code we do not expect, then we are trouble ++ * If we see a code we do not expect, then we are trouble. + */ + goto next_log_record_analyze; + } +@@ -4659,7 +4668,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * Scan the Dirty Page Table and Transaction Table for +- * the lowest lsn, and return it as the Redo lsn ++ * the lowest lsn, and return it as the Redo lsn. + */ + dp = NULL; + while ((dp = enum_rstbl(dptbl, dp))) { +@@ -4675,7 +4684,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + rlsn = t64; + } + +- /* Only proceed if the Dirty Page Table or Transaction table are not empty */ ++ /* ++ * Only proceed if the Dirty Page Table or Transaction ++ * table are not empty. ++ */ + if ((!dptbl || !dptbl->total) && (!trtbl || !trtbl->total)) + goto end_reply; + +@@ -4683,7 +4695,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (is_ro) + goto out; + +- /* Reopen all of the attributes with dirty pages */ ++ /* Reopen all of the attributes with dirty pages. */ + oe = NULL; + next_open_attribute: + +@@ -4779,8 +4791,8 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto next_open_attribute; + + /* +- * Now loop through the dirty page table to extract all of the Vcn/Lcn +- * Mapping that we have, and insert it into the appropriate run ++ * Now loop through the dirty page table to extract all of the Vcn/Lcn. ++ * Mapping that we have, and insert it into the appropriate run. + */ + next_dirty_page: + dp = enum_rstbl(dptbl, dp); +@@ -4834,8 +4846,8 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + do_redo_1: + /* + * Perform the Redo Pass, to restore all of the dirty pages to the same +- * contents that they had immediately before the crash +- * If the dirty page table is empty, then we can skip the entire Redo Pass ++ * contents that they had immediately before the crash. If the dirty ++ * page table is empty, then we can skip the entire Redo Pass. + */ + if (!dptbl || !dptbl->total) + goto do_undo_action; +@@ -4844,15 +4856,15 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * Read the record at the Redo lsn, before falling +- * into common code to handle each record ++ * into common code to handle each record. + */ + err = read_log_rec_lcb(log, rlsn, lcb_ctx_next, &lcb); + if (err) + goto out; + + /* +- * Now loop to read all of our log records forwards, +- * until we hit the end of the file, cleaning up at the end ++ * Now loop to read all of our log records forwards, until ++ * we hit the end of the file, cleaning up at the end. + */ + do_action_next: + frh = lcb->lrh; +@@ -4869,7 +4881,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto out; + } + +- /* Ignore log records that do not update pages */ ++ /* Ignore log records that do not update pages. */ + if (lrh->lcns_follow) + goto find_dirty_page; + +@@ -4914,11 +4926,11 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + goto read_next_log_do_action; + } + +- /* Point to the Redo data and get its length */ ++ /* Point to the Redo data and get its length. */ + data = Add2Ptr(lrh, le16_to_cpu(lrh->redo_off)); + dlen = le16_to_cpu(lrh->redo_len); + +- /* Shorten length by any Lcns which were deleted */ ++ /* Shorten length by any Lcns which were deleted. */ + saved_len = dlen; + + for (i = le16_to_cpu(lrh->lcns_follow); i; i--) { +@@ -4929,7 +4941,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + le16_to_cpu(lrh->attr_off); + voff += le16_to_cpu(lrh->cluster_off) << SECTOR_SHIFT; + +- /* If the Vcn question is allocated, we can just get out.*/ ++ /* If the Vcn question is allocated, we can just get out. */ + j = le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn); + if (dp->page_lcns[j + i - 1]) + break; +@@ -4939,13 +4951,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * Calculate the allocated space left relative to the +- * log record Vcn, after removing this unallocated Vcn ++ * log record Vcn, after removing this unallocated Vcn. + */ + alen = (i - 1) << sbi->cluster_bits; + + /* + * If the update described this log record goes beyond +- * the allocated space, then we will have to reduce the length ++ * the allocated space, then we will have to reduce the length. + */ + if (voff >= alen) + dlen = 0; +@@ -4953,7 +4965,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + dlen = alen - voff; + } + +- /* If the resulting dlen from above is now zero, we can skip this log record */ ++ /* ++ * If the resulting dlen from above is now zero, ++ * we can skip this log record. ++ */ + if (!dlen && saved_len) + goto read_next_log_do_action; + +@@ -4961,12 +4976,12 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (can_skip_action(t16)) + goto read_next_log_do_action; + +- /* Apply the Redo operation a common routine */ ++ /* Apply the Redo operation a common routine. */ + err = do_action(log, oe, lrh, t16, data, dlen, rec_len, &rec_lsn); + if (err) + goto out; + +- /* Keep reading and looping back until end of file */ ++ /* Keep reading and looping back until end of file. */ + read_next_log_do_action: + err = read_next_log_rec(log, lcb, &rec_lsn); + if (!err && rec_lsn) +@@ -4976,7 +4991,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + lcb = NULL; + + do_undo_action: +- /* Scan Transaction Table */ ++ /* Scan Transaction Table. */ + tr = NULL; + transaction_table_next: + tr = enum_rstbl(trtbl, tr); +@@ -4993,19 +5008,19 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + /* + * We only have to do anything if the transaction has +- * something its undo_next_lsn field ++ * something its undo_next_lsn field. + */ + if (!undo_next_lsn) + goto commit_undo; + +- /* Read the first record to be undone by this transaction */ ++ /* Read the first record to be undone by this transaction. */ + err = read_log_rec_lcb(log, undo_next_lsn, lcb_ctx_undo_next, &lcb); + if (err) + goto out; + + /* + * Now loop to read all of our log records forwards, +- * until we hit the end of the file, cleaning up at the end ++ * until we hit the end of the file, cleaning up at the end. + */ + undo_action_next: + +@@ -5035,7 +5050,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + /* + * If the mapping isn't already the table or the mapping + * corresponds to a hole the mapping, we need to make sure +- * there is no partial page already memory ++ * there is no partial page already memory. + */ + if (is_mapped && lcn != SPARSE_LCN && clen >= t16) + goto add_allocated_vcns; +@@ -5063,17 +5078,17 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (can_skip_action(t16)) + goto read_next_log_undo_action; + +- /* Point to the Redo data and get its length */ ++ /* Point to the Redo data and get its length. */ + data = Add2Ptr(lrh, le16_to_cpu(lrh->undo_off)); + dlen = le16_to_cpu(lrh->undo_len); + +- /* it is time to apply the undo action */ ++ /* It is time to apply the undo action. */ + err = do_action(log, oe, lrh, t16, data, dlen, rec_len, NULL); + + read_next_log_undo_action: + /* + * Keep reading and looping back until we have read the +- * last record for this transaction ++ * last record for this transaction. + */ + err = read_next_log_rec(log, lcb, &rec_lsn); + if (err) +@@ -5148,7 +5163,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + if (lcb) + lcb_put(lcb); + +- /* Scan the Open Attribute Table to close all of the open attributes */ ++ /* ++ * Scan the Open Attribute Table to close all of ++ * the open attributes. ++ */ + oe = NULL; + while ((oe = enum_rstbl(oatbl, oe))) { + rno = ino_get(&oe->ref); +diff --git a/src/fsntfs.c b/src/fsntfs.c +index fb2652c8dd74c9934a73d0076409a95e3bb6815f..0edb95ed9717f442f97b3ba3e85b4b6a361e0e27 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -101,9 +101,7 @@ const __le16 WOF_NAME[17] = { + // clang-format on + + /* +- * ntfs_fix_pre_write +- * +- * inserts fixups into 'rhdr' before writing to disk ++ * ntfs_fix_pre_write - Insert fixups into @rhdr before writing to disk. + */ + bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes) + { +@@ -117,7 +115,7 @@ bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes) + return false; + } + +- /* Get fixup pointer */ ++ /* Get fixup pointer. */ + fixup = Add2Ptr(rhdr, fo); + + if (*fixup >= 0x7FFF) +@@ -138,10 +136,9 @@ bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes) + } + + /* +- * ntfs_fix_post_read ++ * ntfs_fix_post_read - Remove fixups after reading from disk. + * +- * remove fixups after reading from disk +- * Returns < 0 if error, 0 if ok, 1 if need to update fixups ++ * Return: < 0 if error, 0 if ok, 1 if need to update fixups. + */ + int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, + bool simple) +@@ -154,26 +151,26 @@ int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, + fn = simple ? ((bytes >> SECTOR_SHIFT) + 1) + : le16_to_cpu(rhdr->fix_num); + +- /* Check errors */ ++ /* Check errors. */ + if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || + fn * SECTOR_SIZE > bytes) { +- return -EINVAL; /* native chkntfs returns ok! */ ++ return -EINVAL; /* Native chkntfs returns ok! */ + } + +- /* Get fixup pointer */ ++ /* Get fixup pointer. */ + fixup = Add2Ptr(rhdr, fo); + sample = *fixup; + ptr = Add2Ptr(rhdr, SECTOR_SIZE - sizeof(short)); + ret = 0; + + while (fn--) { +- /* Test current word */ ++ /* Test current word. */ + if (*ptr != sample) { + /* Fixup does not match! Is it serious error? */ + ret = -E_NTFS_FIXUP; + } + +- /* Replace fixup */ ++ /* Replace fixup. */ + *ptr = *++fixup; + ptr += SECTOR_SIZE / sizeof(short); + } +@@ -182,9 +179,7 @@ int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, + } + + /* +- * ntfs_extend_init +- * +- * loads $Extend file ++ * ntfs_extend_init - Load $Extend file. + */ + int ntfs_extend_init(struct ntfs_sb_info *sbi) + { +@@ -209,7 +204,7 @@ int ntfs_extend_init(struct ntfs_sb_info *sbi) + goto out; + } + +- /* if ntfs_iget5 reads from disk it never returns bad inode */ ++ /* If ntfs_iget5() reads from disk it never returns bad inode. */ + if (!S_ISDIR(inode->i_mode)) { + err = -EINVAL; + goto out; +@@ -261,7 +256,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) + struct MFT_REF ref; + struct inode *inode; + +- /* Check for 4GB */ ++ /* Check for 4GB. */ + if (ni->vfs_inode.i_size >= 0x100000000ull) { + ntfs_err(sb, "\x24LogFile is too big"); + err = -EINVAL; +@@ -280,7 +275,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) + inode = NULL; + + if (!inode) { +- /* Try to use mft copy */ ++ /* Try to use MFT copy. */ + u64 t64 = sbi->mft.lbo; + + sbi->mft.lbo = sbi->mft.lbo2; +@@ -298,7 +293,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) + + sbi->mft.ni = ntfs_i(inode); + +- /* LogFile should not contains attribute list */ ++ /* LogFile should not contains attribute list. */ + err = ni_load_all_mi(sbi->mft.ni); + if (!err) + err = log_replay(ni, &initialized); +@@ -317,7 +312,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) + if (sb_rdonly(sb) || !initialized) + goto out; + +- /* fill LogFile by '-1' if it is initialized */ ++ /* Fill LogFile by '-1' if it is initialized.ssss */ + err = ntfs_bio_fill_1(sbi, &ni->file.run); + + out: +@@ -329,7 +324,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) + /* + * ntfs_query_def + * +- * returns current ATTR_DEF_ENTRY for given attribute type ++ * Return: Current ATTR_DEF_ENTRY for given attribute type. + */ + const struct ATTR_DEF_ENTRY *ntfs_query_def(struct ntfs_sb_info *sbi, + enum ATTR_TYPE type) +@@ -356,9 +351,7 @@ const struct ATTR_DEF_ENTRY *ntfs_query_def(struct ntfs_sb_info *sbi, + } + + /* +- * ntfs_look_for_free_space +- * +- * looks for a free space in bitmap ++ * ntfs_look_for_free_space - Look for a free space in bitmap. + */ + int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + CLST *new_lcn, CLST *new_len, +@@ -406,7 +399,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + + /* + * 'Cause cluster 0 is always used this value means that we should use +- * cached value of 'next_free_lcn' to improve performance ++ * cached value of 'next_free_lcn' to improve performance. + */ + if (!lcn) + lcn = sbi->used.next_free_lcn; +@@ -420,18 +413,18 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + goto ok; + } + +- /* Try to use clusters from MftZone */ ++ /* Try to use clusters from MftZone. */ + zlen = wnd_zone_len(wnd); + zeroes = wnd_zeroes(wnd); + +- /* Check too big request */ ++ /* Check too big request. */ + if (len > zeroes + zlen) + goto no_space; + + if (zlen <= NTFS_MIN_MFT_ZONE) + goto no_space; + +- /* How many clusters to cat from zone */ ++ /* How many clusters to cat from zone. */ + zlcn = wnd_zone_bit(wnd); + zlen2 = zlen >> 1; + ztrim = len > zlen ? zlen : (len > zlen2 ? len : zlen2); +@@ -445,7 +438,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + + wnd_zone_set(wnd, zlcn, new_zlen); + +- /* allocate continues clusters */ ++ /* Allocate continues clusters. */ + *new_len = + wnd_find(wnd, len, 0, + BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &a_lcn); +@@ -467,7 +460,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + if (opt & ALLOCATE_MFT) + goto out; + +- /* Set hint for next requests */ ++ /* Set hint for next requests. */ + sbi->used.next_free_lcn = *new_lcn + *new_len; + + out: +@@ -476,10 +469,9 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + } + + /* +- * ntfs_extend_mft ++ * ntfs_extend_mft - Allocate additional MFT records. + * +- * allocates additional MFT records +- * sbi->mft.bitmap is locked for write ++ * sbi->mft.bitmap is locked for write. + * + * NOTE: recursive: + * ntfs_look_free_mft -> +@@ -490,8 +482,9 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + * ni_ins_attr_ext -> + * ntfs_look_free_mft -> + * ntfs_extend_mft +- * To avoid recursive always allocate space for two new mft records +- * see attrib.c: "at least two mft to avoid recursive loop" ++ * ++ * To avoid recursive always allocate space for two new MFT records ++ * see attrib.c: "at least two MFT to avoid recursive loop". + */ + static int ntfs_extend_mft(struct ntfs_sb_info *sbi) + { +@@ -505,7 +498,7 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi) + new_mft_total = (wnd->nbits + MFT_INCREASE_CHUNK + 127) & (CLST)~127; + new_mft_bytes = (u64)new_mft_total << sbi->record_bits; + +- /* Step 1: Resize $MFT::DATA */ ++ /* Step 1: Resize $MFT::DATA. */ + down_write(&ni->file.run_lock); + err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, + new_mft_bytes, NULL, false, &attr); +@@ -519,13 +512,13 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi) + new_mft_total = le64_to_cpu(attr->nres.alloc_size) >> sbi->record_bits; + ni->mi.dirty = true; + +- /* Step 2: Resize $MFT::BITMAP */ ++ /* Step 2: Resize $MFT::BITMAP. */ + new_bitmap_bytes = bitmap_size(new_mft_total); + + err = attr_set_size(ni, ATTR_BITMAP, NULL, 0, &sbi->mft.bitmap.run, + new_bitmap_bytes, &new_bitmap_bytes, true, NULL); + +- /* Refresh Mft Zone if necessary */ ++ /* Refresh MFT Zone if necessary. */ + down_write_nested(&sbi->used.bitmap.rw_lock, BITMAP_MUTEX_CLUSTERS); + + ntfs_refresh_zone(sbi); +@@ -549,9 +542,7 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi) + } + + /* +- * ntfs_look_free_mft +- * +- * looks for a free MFT record ++ * ntfs_look_free_mft - Look for a free MFT record. + */ + int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + struct ntfs_inode *ni, struct mft_inode **mi) +@@ -572,7 +563,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + + zlen = wnd_zone_len(wnd); + +- /* Always reserve space for MFT */ ++ /* Always reserve space for MFT. */ + if (zlen) { + if (mft) { + zbit = wnd_zone_bit(wnd); +@@ -582,7 +573,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + goto found; + } + +- /* No MFT zone. find the nearest to '0' free MFT */ ++ /* No MFT zone. Find the nearest to '0' free MFT. */ + if (!wnd_find(wnd, 1, MFT_REC_FREE, 0, &zbit)) { + /* Resize MFT */ + mft_total = wnd->nbits; +@@ -601,10 +592,10 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + /* + * Look for free record reserved area [11-16) == + * [MFT_REC_RESERVED, MFT_REC_FREE ) MFT bitmap always +- * marks it as used ++ * marks it as used. + */ + if (!sbi->mft.reserved_bitmap) { +- /* Once per session create internal bitmap for 5 bits */ ++ /* Once per session create internal bitmap for 5 bits. */ + sbi->mft.reserved_bitmap = 0xFF; + + ref.high = 0; +@@ -671,7 +662,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + while (zlen > 1 && !wnd_is_free(wnd, zbit, zlen)) + zlen -= 1; + +- /* [zbit, zbit + zlen) will be used for Mft itself */ ++ /* [zbit, zbit + zlen) will be used for MFT itself. */ + from = sbi->mft.used; + if (from < zbit) + from = zbit; +@@ -692,7 +683,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + + found: + if (!mft) { +- /* The request to get record for general purpose */ ++ /* The request to get record for general purpose. */ + if (sbi->mft.next_free < MFT_REC_USER) + sbi->mft.next_free = MFT_REC_USER; + +@@ -717,7 +708,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + goto out; + } + +- /* We have found a record that are not reserved for next MFT */ ++ /* We have found a record that are not reserved for next MFT. */ + if (*rno >= MFT_REC_FREE) + wnd_set_used(wnd, *rno, 1); + else if (*rno >= MFT_REC_RESERVED && sbi->mft.reserved_bitmap_inited) +@@ -731,9 +722,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, + } + + /* +- * ntfs_mark_rec_free +- * +- * marks record as free ++ * ntfs_mark_rec_free - Mark record as free. + */ + void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno) + { +@@ -762,10 +751,9 @@ void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno) + } + + /* +- * ntfs_clear_mft_tail ++ * ntfs_clear_mft_tail - Format empty records [from, to). + * +- * formats empty records [from, to) +- * sbi->mft.bitmap is locked for write ++ * sbi->mft.bitmap is locked for write. + */ + int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to) + { +@@ -804,12 +792,11 @@ int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to) + } + + /* +- * ntfs_refresh_zone ++ * ntfs_refresh_zone - Refresh MFT zone. + * +- * refreshes Mft zone +- * sbi->used.bitmap is locked for rw +- * sbi->mft.bitmap is locked for write +- * sbi->mft.ni->file.run_lock for write ++ * sbi->used.bitmap is locked for rw. ++ * sbi->mft.bitmap is locked for write. ++ * sbi->mft.ni->file.run_lock for write. + */ + int ntfs_refresh_zone(struct ntfs_sb_info *sbi) + { +@@ -818,14 +805,14 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi) + struct wnd_bitmap *wnd = &sbi->used.bitmap; + struct ntfs_inode *ni = sbi->mft.ni; + +- /* Do not change anything unless we have non empty Mft zone */ ++ /* Do not change anything unless we have non empty MFT zone. */ + if (wnd_zone_len(wnd)) + return 0; + + /* +- * Compute the mft zone at two steps +- * It would be nice if we are able to allocate +- * 1/8 of total clusters for MFT but not more then 512 MB ++ * Compute the MFT zone at two steps. ++ * It would be nice if we are able to allocate 1/8 of ++ * total clusters for MFT but not more then 512 MB. + */ + zone_limit = (512 * 1024 * 1024) >> sbi->cluster_bits; + zone_max = wnd->nbits >> 3; +@@ -838,29 +825,27 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi) + if (!run_lookup_entry(&ni->file.run, vcn - 1, &lcn, &len, NULL)) + lcn = SPARSE_LCN; + +- /* We should always find Last Lcn for MFT */ ++ /* We should always find Last Lcn for MFT. */ + if (lcn == SPARSE_LCN) + return -EINVAL; + + lcn_s = lcn + 1; + +- /* Try to allocate clusters after last MFT run */ ++ /* Try to allocate clusters after last MFT run. */ + zlen = wnd_find(wnd, zone_max, lcn_s, 0, &lcn_s); + if (!zlen) { + ntfs_notice(sbi->sb, "MftZone: unavailable"); + return 0; + } + +- /* Truncate too large zone */ ++ /* Truncate too large zone. */ + wnd_zone_set(wnd, lcn_s, zlen); + + return 0; + } + + /* +- * ntfs_update_mftmirr +- * +- * updates $MFTMirr data ++ * ntfs_update_mftmirr - Update $MFTMirr data. + */ + int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) + { +@@ -923,9 +908,9 @@ int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) + /* + * ntfs_set_state + * +- * mount: ntfs_set_state(NTFS_DIRTY_DIRTY) +- * umount: ntfs_set_state(NTFS_DIRTY_CLEAR) +- * ntfs error: ntfs_set_state(NTFS_DIRTY_ERROR) ++ * Mount: ntfs_set_state(NTFS_DIRTY_DIRTY) ++ * Umount: ntfs_set_state(NTFS_DIRTY_CLEAR) ++ * NTFS error: ntfs_set_state(NTFS_DIRTY_ERROR) + */ + int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) + { +@@ -936,14 +921,14 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) + struct ntfs_inode *ni; + + /* +- * do not change state if fs was real_dirty +- * do not change state if fs already dirty(clear) +- * do not change any thing if mounted read only ++ * Do not change state if fs was real_dirty. ++ * Do not change state if fs already dirty(clear). ++ * Do not change any thing if mounted read only. + */ + if (sbi->volume.real_dirty || sb_rdonly(sbi->sb)) + return 0; + +- /* Check cached value */ ++ /* Check cached value. */ + if ((dirty == NTFS_DIRTY_CLEAR ? 0 : VOLUME_FLAG_DIRTY) == + (sbi->volume.flags & VOLUME_FLAG_DIRTY)) + return 0; +@@ -978,7 +963,7 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) + info->flags &= ~VOLUME_FLAG_DIRTY; + break; + } +- /* cache current volume flags*/ ++ /* Cache current volume flags. */ + sbi->volume.flags = info->flags; + mi->dirty = true; + err = 0; +@@ -989,7 +974,7 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) + return err; + + mark_inode_dirty(&ni->vfs_inode); +- /*verify(!ntfs_update_mftmirr()); */ ++ /* verify(!ntfs_update_mftmirr()); */ + + /* + * if we used wait=1, sync_inode_metadata waits for the io for the +@@ -1005,9 +990,7 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) + } + + /* +- * security_hash +- * +- * calculates a hash of security descriptor ++ * security_hash - Calculates a hash of security descriptor. + */ + static inline __le32 security_hash(const void *sd, size_t bytes) + { +@@ -1193,13 +1176,13 @@ int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run, + struct buffer_head *bh; + + if (!run) { +- /* first reading of $Volume + $MFTMirr + LogFile goes here*/ ++ /* First reading of $Volume + $MFTMirr + $LogFile goes here. */ + if (vbo > MFT_REC_VOL * sbi->record_size) { + err = -ENOENT; + goto out; + } + +- /* use absolute boot's 'MFTCluster' to read record */ ++ /* Use absolute boot's 'MFTCluster' to read record. */ + lbo = vbo + sbi->mft.lbo; + len = sbi->record_size; + } else if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { +@@ -1290,7 +1273,11 @@ int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run, + return err; + } + +-/* Returns < 0 if error, 0 if ok, '-E_NTFS_FIXUP' if need to update fixups */ ++/* ++ * ntfs_read_bh ++ * ++ * Return: < 0 if error, 0 if ok, -E_NTFS_FIXUP if need to update fixups. ++ */ + int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, + struct NTFS_RECORD_HEADER *rhdr, u32 bytes, + struct ntfs_buffers *nb) +@@ -1487,7 +1474,9 @@ static inline struct bio *ntfs_alloc_bio(u32 nr_vecs) + return bio; + } + +-/* read/write pages from/to disk*/ ++/* ++ * ntfs_bio_pages - Read/write pages from/to disk. ++ */ + int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run, + struct page **pages, u32 nr_pages, u64 vbo, u32 bytes, + u32 op) +@@ -1509,7 +1498,7 @@ int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run, + + blk_start_plug(&plug); + +- /* align vbo and bytes to be 512 bytes aligned */ ++ /* Align vbo and bytes to be 512 bytes aligned. */ + lbo = (vbo + bytes + 511) & ~511ull; + vbo = vbo & ~511ull; + bytes = lbo - vbo; +@@ -1588,9 +1577,10 @@ int ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run, + } + + /* +- * Helper for ntfs_loadlog_and_replay +- * fill on-disk logfile range by (-1) +- * this means empty logfile ++ * ntfs_bio_fill_1 - Helper for ntfs_loadlog_and_replay(). ++ * ++ * Fill on-disk logfile range by (-1) ++ * this means empty logfile. + */ + int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run) + { +@@ -1622,7 +1612,7 @@ int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run) + } + + /* +- * TODO: try blkdev_issue_write_same ++ * TODO: Try blkdev_issue_write_same. + */ + blk_start_plug(&plug); + do { +@@ -1719,8 +1709,8 @@ struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, bool dir) + + /* + * O:BAG:BAD:(A;OICI;FA;;;WD) +- * owner S-1-5-32-544 (Administrators) +- * group S-1-5-32-544 (Administrators) ++ * Owner S-1-5-32-544 (Administrators) ++ * Group S-1-5-32-544 (Administrators) + * ACE: allow S-1-1-0 (Everyone) with FILE_ALL_ACCESS + */ + const u8 s_default_security[] __aligned(8) = { +@@ -1741,7 +1731,9 @@ static inline u32 sid_length(const struct SID *sid) + } + + /* +- * Thanks Mark Harmstone for idea ++ * is_acl_valid ++ * ++ * Thanks Mark Harmstone for idea. + */ + static bool is_acl_valid(const struct ACL *acl, u32 len) + { +@@ -1857,9 +1849,7 @@ bool is_sd_valid(const struct SECURITY_DESCRIPTOR_RELATIVE *sd, u32 len) + } + + /* +- * ntfs_security_init +- * +- * loads and parse $Secure ++ * ntfs_security_init - Load and parse $Secure. + */ + int ntfs_security_init(struct ntfs_sb_info *sbi) + { +@@ -1940,9 +1930,9 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + + sds_size = inode->i_size; + +- /* Find the last valid Id */ ++ /* Find the last valid Id. */ + sbi->security.next_id = SECURITY_ID_FIRST; +- /* Always write new security at the end of bucket */ ++ /* Always write new security at the end of bucket. */ + sbi->security.next_off = + ALIGN(sds_size - SecurityDescriptorsBlockSize, 16); + +@@ -1975,9 +1965,7 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + } + + /* +- * ntfs_get_security_by_id +- * +- * reads security descriptor by id ++ * ntfs_get_security_by_id - Read security descriptor by id. + */ + int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, + struct SECURITY_DESCRIPTOR_RELATIVE **sd, +@@ -2010,7 +1998,7 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, + goto out; + } + +- /* Try to find this SECURITY descriptor in SII indexes */ ++ /* Try to find this SECURITY descriptor in SII indexes. */ + err = indx_find(indx, ni, root_sii, &security_id, sizeof(security_id), + NULL, &diff, (struct NTFS_DE **)&sii_e, fnd_sii); + if (err) +@@ -2026,9 +2014,7 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, + } + + if (t32 > SIZEOF_SECURITY_HDR + 0x10000) { +- /* +- * looks like too big security. 0x10000 - is arbitrary big number +- */ ++ /* Looks like too big security. 0x10000 - is arbitrary big number. */ + err = -EFBIG; + goto out; + } +@@ -2071,9 +2057,7 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, + } + + /* +- * ntfs_insert_security +- * +- * inserts security descriptor into $Secure::SDS ++ * ntfs_insert_security - Insert security descriptor into $Secure::SDS. + * + * SECURITY Descriptor Stream data is organized into chunks of 256K bytes + * and it contains a mirror copy of each security descriptor. When writing +@@ -2114,7 +2098,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + *inserted = false; + *security_id = SECURITY_ID_INVALID; + +- /* Allocate a temporal buffer*/ ++ /* Allocate a temporal buffer. */ + d_security = kzalloc(aligned_sec_size, GFP_NOFS); + if (!d_security) + return -ENOMEM; +@@ -2140,8 +2124,8 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + } + + /* +- * Check if such security already exists +- * use "SDH" and hash -> to get the offset in "SDS" ++ * Check if such security already exists. ++ * Use "SDH" and hash -> to get the offset in "SDS". + */ + err = indx_find(indx_sdh, ni, root_sdh, &hash_key, sizeof(hash_key), + &d_security->key.sec_id, &diff, (struct NTFS_DE **)&e, +@@ -2161,7 +2145,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + d_security->key.hash == hash_key.hash && + !memcmp(d_security + 1, sd, size_sd)) { + *security_id = d_security->key.sec_id; +- /*such security already exists*/ ++ /* Such security already exists. */ + err = 0; + goto out; + } +@@ -2176,17 +2160,17 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + break; + } + +- /* Zero unused space */ ++ /* Zero unused space. */ + next = sbi->security.next_off & (SecurityDescriptorsBlockSize - 1); + left = SecurityDescriptorsBlockSize - next; + +- /* Zero gap until SecurityDescriptorsBlockSize */ ++ /* Zero gap until SecurityDescriptorsBlockSize. */ + if (left < new_sec_size) { +- /* zero "left" bytes from sbi->security.next_off */ ++ /* Zero "left" bytes from sbi->security.next_off. */ + sbi->security.next_off += SecurityDescriptorsBlockSize + left; + } + +- /* Zero tail of previous security */ ++ /* Zero tail of previous security. */ + //used = ni->vfs_inode.i_size & (SecurityDescriptorsBlockSize - 1); + + /* +@@ -2199,14 +2183,14 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + * zero "tozero" bytes from sbi->security.next_off - tozero + */ + +- /* format new security descriptor */ ++ /* Format new security descriptor. */ + d_security->key.hash = hash_key.hash; + d_security->key.sec_id = cpu_to_le32(sbi->security.next_id); + d_security->off = cpu_to_le64(sbi->security.next_off); + d_security->size = cpu_to_le32(new_sec_size); + memcpy(d_security + 1, sd, size_sd); + +- /* Write main SDS bucket */ ++ /* Write main SDS bucket. */ + err = ntfs_sb_write_run(sbi, &ni->file.run, sbi->security.next_off, + d_security, aligned_sec_size); + +@@ -2224,13 +2208,13 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + goto out; + } + +- /* Write copy SDS bucket */ ++ /* Write copy SDS bucket. */ + err = ntfs_sb_write_run(sbi, &ni->file.run, mirr_off, d_security, + aligned_sec_size); + if (err) + goto out; + +- /* Fill SII entry */ ++ /* Fill SII entry. */ + sii_e.de.view.data_off = + cpu_to_le16(offsetof(struct NTFS_DE_SII, sec_hdr)); + sii_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR); +@@ -2246,7 +2230,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + if (err) + goto out; + +- /* Fill SDH entry */ ++ /* Fill SDH entry. */ + sdh_e.de.view.data_off = + cpu_to_le16(offsetof(struct NTFS_DE_SDH, sec_hdr)); + sdh_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR); +@@ -2271,7 +2255,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + if (inserted) + *inserted = true; + +- /* Update Id and offset for next descriptor */ ++ /* Update Id and offset for next descriptor. */ + sbi->security.next_id += 1; + sbi->security.next_off += aligned_sec_size; + +@@ -2285,9 +2269,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + } + + /* +- * ntfs_reparse_init +- * +- * loads and parse $Extend/$Reparse ++ * ntfs_reparse_init - Load and parse $Extend/$Reparse. + */ + int ntfs_reparse_init(struct ntfs_sb_info *sbi) + { +@@ -2325,9 +2307,7 @@ int ntfs_reparse_init(struct ntfs_sb_info *sbi) + } + + /* +- * ntfs_objid_init +- * +- * loads and parse $Extend/$ObjId ++ * ntfs_objid_init - Load and parse $Extend/$ObjId. + */ + int ntfs_objid_init(struct ntfs_sb_info *sbi) + { +@@ -2449,14 +2429,14 @@ int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag, + goto out; + } + +- /* 1 - forces to ignore rkey.ReparseTag when comparing keys */ ++ /* 1 - forces to ignore rkey.ReparseTag when comparing keys. */ + err = indx_find(indx, ni, root_r, &rkey, sizeof(rkey), (void *)1, &diff, + (struct NTFS_DE **)&re, fnd); + if (err) + goto out; + + if (memcmp(&re->key.ref, ref, sizeof(*ref))) { +- /* Impossible. Looks like volume corrupt?*/ ++ /* Impossible. Looks like volume corrupt? */ + goto out; + } + +@@ -2528,9 +2508,7 @@ void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim) + } + + /* +- * run_deallocate +- * +- * deallocate clusters ++ * run_deallocate - Deallocate clusters. + */ + int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim) + { +diff --git a/src/index.c b/src/index.c +index 69c6c4e0b4d9bd76b79de148f164fec98b7fac9f..70ef59455b72c927b40bbb76c5420b8dfc72e24a 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -24,9 +24,10 @@ static const struct INDEX_NAMES { + }; + + /* +- * compare two names in index ++ * cmp_fnames - Compare two names in index. ++ * + * if l1 != 0 +- * both names are little endian on-disk ATTR_FILE_NAME structs ++ * Both names are little endian on-disk ATTR_FILE_NAME structs. + * else + * key1 - cpu_str, key2 - ATTR_FILE_NAME + */ +@@ -52,7 +53,7 @@ static int cmp_fnames(const void *key1, size_t l1, const void *key2, size_t l2, + + /* + * If names are equal (case insensitive) +- * try to compare it case sensitive ++ * try to compare it case sensitive. + */ + return ntfs_cmp_names_cpu(key1, s2, sbi->upcase, both_case); + } +@@ -62,7 +63,9 @@ static int cmp_fnames(const void *key1, size_t l1, const void *key2, size_t l2, + sbi->upcase, both_case); + } + +-/* $SII of $Secure and $Q of Quota */ ++/* ++ * cmp_uint - $SII of $Secure and $Q of Quota ++ */ + static int cmp_uint(const void *key1, size_t l1, const void *key2, size_t l2, + const void *data) + { +@@ -79,7 +82,9 @@ static int cmp_uint(const void *key1, size_t l1, const void *key2, size_t l2, + return 0; + } + +-/* $SDH of $Secure */ ++/* ++ * cmp_sdh - $SDH of $Secure ++ */ + static int cmp_sdh(const void *key1, size_t l1, const void *key2, size_t l2, + const void *data) + { +@@ -93,13 +98,13 @@ static int cmp_sdh(const void *key1, size_t l1, const void *key2, size_t l2, + t1 = le32_to_cpu(k1->hash); + t2 = le32_to_cpu(k2->hash); + +- /* First value is a hash value itself */ ++ /* First value is a hash value itself. */ + if (t1 < t2) + return -1; + if (t1 > t2) + return 1; + +- /* Second value is security Id */ ++ /* Second value is security Id. */ + if (data) { + t1 = le32_to_cpu(k1->sec_id); + t2 = le32_to_cpu(k2->sec_id); +@@ -112,7 +117,9 @@ static int cmp_sdh(const void *key1, size_t l1, const void *key2, size_t l2, + return 0; + } + +-/* $O of ObjId and "$R" for Reparse */ ++/* ++ * cmp_uints - $O of ObjId and "$R" for Reparse. ++ */ + static int cmp_uints(const void *key1, size_t l1, const void *key2, size_t l2, + const void *data) + { +@@ -122,12 +129,13 @@ static int cmp_uints(const void *key1, size_t l1, const void *key2, size_t l2, + + if ((size_t)data == 1) { + /* +- * ni_delete_all -> ntfs_remove_reparse -> delete all with this reference ++ * ni_delete_all -> ntfs_remove_reparse -> ++ * delete all with this reference. + * k1, k2 - pointers to REPARSE_KEY + */ + +- k1 += 1; // skip REPARSE_KEY.ReparseTag +- k2 += 1; // skip REPARSE_KEY.ReparseTag ++ k1 += 1; // Skip REPARSE_KEY.ReparseTag ++ k2 += 1; // Skip REPARSE_KEY.ReparseTag + if (l2 <= sizeof(int)) + return -1; + l2 -= sizeof(int); +@@ -229,7 +237,7 @@ static int bmp_buf_get(struct ntfs_index *indx, struct ntfs_inode *ni, + + data_size = le64_to_cpu(b->nres.data_size); + if (WARN_ON(off >= data_size)) { +- /* looks like filesystem error */ ++ /* Looks like filesystem error. */ + return -EINVAL; + } + +@@ -303,9 +311,7 @@ static void bmp_buf_put(struct bmp_buf *bbuf, bool dirty) + } + + /* +- * indx_mark_used +- * +- * marks the bit 'bit' as used ++ * indx_mark_used - Mark the bit @bit as used. + */ + static int indx_mark_used(struct ntfs_index *indx, struct ntfs_inode *ni, + size_t bit) +@@ -325,9 +331,7 @@ static int indx_mark_used(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * indx_mark_free +- * +- * the bit 'bit' as free ++ * indx_mark_free - Mark the bit @bit as free. + */ + static int indx_mark_free(struct ntfs_index *indx, struct ntfs_inode *ni, + size_t bit) +@@ -347,9 +351,11 @@ static int indx_mark_free(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * if ntfs_readdir calls this function (indx_used_bit -> scan_nres_bitmap), +- * inode is shared locked and no ni_lock +- * use rw_semaphore for read/write access to bitmap_run ++ * scan_nres_bitmap ++ * ++ * If ntfs_readdir calls this function (indx_used_bit -> scan_nres_bitmap), ++ * inode is shared locked and no ni_lock. ++ * Use rw_semaphore for read/write access to bitmap_run. + */ + static int scan_nres_bitmap(struct ntfs_inode *ni, struct ATTRIB *bitmap, + struct ntfs_index *indx, size_t from, +@@ -460,10 +466,9 @@ static bool scan_for_free(const ulong *buf, u32 bit, u32 bits, size_t *ret) + } + + /* +- * indx_find_free ++ * indx_find_free - Look for free bit. + * +- * looks for free bit +- * returns -1 if no free bits ++ * Return: -1 if no free bits. + */ + static int indx_find_free(struct ntfs_index *indx, struct ntfs_inode *ni, + size_t *bit, struct ATTRIB **bitmap) +@@ -509,10 +514,9 @@ static bool scan_for_used(const ulong *buf, u32 bit, u32 bits, size_t *ret) + } + + /* +- * indx_used_bit ++ * indx_used_bit - Look for used bit. + * +- * looks for used bit +- * returns MINUS_ONE_T if no used bits ++ * Return: MINUS_ONE_T if no used bits. + */ + int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit) + { +@@ -548,9 +552,8 @@ int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit) + /* + * hdr_find_split + * +- * finds a point at which the index allocation buffer would like to +- * be split. +- * NOTE: This function should never return 'END' entry NULL returns on error ++ * Find a point at which the index allocation buffer would like to be split. ++ * NOTE: This function should never return 'END' entry NULL returns on error. + */ + static const struct NTFS_DE *hdr_find_split(const struct INDEX_HDR *hdr) + { +@@ -568,7 +571,7 @@ static const struct NTFS_DE *hdr_find_split(const struct INDEX_HDR *hdr) + + e = Add2Ptr(hdr, o); + +- /* We must not return END entry */ ++ /* We must not return END entry. */ + if (de_is_last(e)) + return p; + +@@ -579,9 +582,8 @@ static const struct NTFS_DE *hdr_find_split(const struct INDEX_HDR *hdr) + } + + /* +- * hdr_insert_head ++ * hdr_insert_head - Insert some entries at the beginning of the buffer. + * +- * inserts some entries at the beginning of the buffer. + * It is used to insert entries into a newly-created buffer. + */ + static const struct NTFS_DE *hdr_insert_head(struct INDEX_HDR *hdr, +@@ -656,14 +658,14 @@ static bool fnd_is_empty(struct ntfs_fnd *fnd) + } + + /* +- * hdr_find_e ++ * hdr_find_e - Locate an entry the index buffer. + * +- * locates an entry the index buffer. + * If no matching entry is found, it returns the first entry which is greater + * than the desired entry If the search key is greater than all the entries the + * buffer, it returns the 'end' entry. This function does a binary search of the +- * current index buffer, for the first entry that is <= to the search value +- * Returns NULL if error ++ * current index buffer, for the first entry that is <= to the search value. ++ * ++ * Return: NULL if error. + */ + static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + const struct INDEX_HDR *hdr, const void *key, +@@ -687,7 +689,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + if (!offs) + goto next; + +- /* use binary search algorithm */ ++ /* Use binary search algorithm. */ + next1: + if (off + sizeof(struct NTFS_DE) > end) { + e = NULL; +@@ -715,7 +717,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + goto next; + } + +- /* Store entry table */ ++ /* Store entry table. */ + offs[max_idx] = off; + + if (!de_is_last(e)) { +@@ -725,8 +727,8 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + } + + /* +- * Table of pointers is created +- * Use binary search to find entry that is <= to the search value ++ * Table of pointers is created. ++ * Use binary search to find entry that is <= to the search value. + */ + fnd = -1; + min_idx = 0; +@@ -772,8 +774,9 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + + next: + /* +- * Entries index are sorted +- * Enumerate all entries until we find entry that is <= to the search value ++ * Entries index are sorted. ++ * Enumerate all entries until we find entry ++ * that is <= to the search value. + */ + if (off + sizeof(struct NTFS_DE) > end) + return NULL; +@@ -803,10 +806,9 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + } + + /* +- * hdr_insert_de ++ * hdr_insert_de - Insert an index entry into the buffer. + * +- * inserts an index entry into the buffer. +- * 'before' should be a pointer previously returned from hdr_find_e ++ * 'before' should be a pointer previously returned from hdr_find_e. + */ + static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, + struct INDEX_HDR *hdr, +@@ -819,20 +821,20 @@ static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, + u32 total = le32_to_cpu(hdr->total); + u16 de_size = le16_to_cpu(de->size); + +- /* First, check to see if there's enough room */ ++ /* First, check to see if there's enough room. */ + if (used + de_size > total) + return NULL; + + /* We know there's enough space, so we know we'll succeed. */ + if (before) { +- /* Check that before is inside Index */ ++ /* Check that before is inside Index. */ + if (off >= used || off < le32_to_cpu(hdr->de_off) || + off + le16_to_cpu(before->size) > total) { + return NULL; + } + goto ok; + } +- /* No insert point is applied. Get it manually */ ++ /* No insert point is applied. Get it manually. */ + before = hdr_find_e(indx, hdr, de + 1, le16_to_cpu(de->key_size), ctx, + &diff); + if (!before) +@@ -850,9 +852,7 @@ static struct NTFS_DE *hdr_insert_de(const struct ntfs_index *indx, + } + + /* +- * hdr_delete_de +- * +- * removes an entry from the index buffer ++ * hdr_delete_de - Remove an entry from the index buffer. + */ + static inline struct NTFS_DE *hdr_delete_de(struct INDEX_HDR *hdr, + struct NTFS_DE *re) +@@ -884,7 +884,7 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, + u32 t32; + const struct INDEX_ROOT *root = resident_data(attr); + +- /* Check root fields */ ++ /* Check root fields. */ + if (!root->index_block_clst) + return -EINVAL; + +@@ -894,13 +894,13 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, + t32 = le32_to_cpu(root->index_block_size); + indx->index_bits = blksize_bits(t32); + +- /* Check index record size */ ++ /* Check index record size. */ + if (t32 < sbi->cluster_size) { +- /* index record is smaller than a cluster, use 512 blocks */ ++ /* Index record is smaller than a cluster, use 512 blocks. */ + if (t32 != root->index_block_clst * SECTOR_SIZE) + return -EINVAL; + +- /* Check alignment to a cluster */ ++ /* Check alignment to a cluster. */ + if ((sbi->cluster_size >> SECTOR_SHIFT) & + (root->index_block_clst - 1)) { + return -EINVAL; +@@ -908,7 +908,7 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi, + + indx->vbn2vbo_bits = SECTOR_SHIFT; + } else { +- /* index record must be a multiple of cluster size */ ++ /* Index record must be a multiple of cluster size. */ + if (t32 != root->index_block_clst << sbi->cluster_bits) + return -EINVAL; + +@@ -953,7 +953,7 @@ static struct indx_node *indx_new(struct ntfs_index *indx, + return ERR_PTR(err); + } + +- /* Create header */ ++ /* Create header. */ + index->rhdr.sign = NTFS_INDX_SIGNATURE; + index->rhdr.fix_off = cpu_to_le16(sizeof(struct INDEX_BUFFER)); // 0x28 + fn = (bytes >> SECTOR_SHIFT) + 1; // 9 +@@ -1011,9 +1011,11 @@ static int indx_write(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * if ntfs_readdir calls this function +- * inode is shared locked and no ni_lock +- * use rw_semaphore for read/write access to alloc_run ++ * indx_read ++ * ++ * If ntfs_readdir calls this function ++ * inode is shared locked and no ni_lock. ++ * Use rw_semaphore for read/write access to alloc_run. + */ + int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn, + struct indx_node **node) +@@ -1095,9 +1097,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn, + } + + /* +- * indx_find +- * +- * scans NTFS directory for given entry ++ * indx_find - Scan NTFS directory for given entry. + */ + int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, + const struct INDEX_ROOT *root, const void *key, size_t key_len, +@@ -1119,7 +1119,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, + + hdr = &root->ihdr; + +- /* Check cache */ ++ /* Check cache. */ + e = fnd->level ? fnd->de[fnd->level - 1] : fnd->root_de; + if (e && !de_is_last(e) && + !(*indx->cmp)(key, key_len, e + 1, le16_to_cpu(e->key_size), ctx)) { +@@ -1128,10 +1128,10 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, + return 0; + } + +- /* Soft finder reset */ ++ /* Soft finder reset. */ + fnd_clear(fnd); + +- /* Lookup entry that is <= to the search value */ ++ /* Lookup entry that is <= to the search value. */ + e = hdr_find_e(indx, hdr, key, key_len, ctx, diff); + if (!e) + return -EINVAL; +@@ -1153,7 +1153,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, + if (err) + goto out; + +- /* Lookup entry that is <= to the search value */ ++ /* Lookup entry that is <= to the search value. */ + e = hdr_find_e(indx, &node->index->ihdr, key, key_len, ctx, + diff); + if (!e) { +@@ -1180,7 +1180,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + int level = fnd->level; + + if (!*entry) { +- /* Start find */ ++ /* Start find. */ + e = hdr_first_de(&root->ihdr); + if (!e) + return 0; +@@ -1210,7 +1210,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + fnd->de[level - 1] = e; + } + +- /* Just to avoid tree cycle */ ++ /* Just to avoid tree cycle. */ + next_iter: + if (iter++ >= 1000) + return -EINVAL; +@@ -1225,12 +1225,12 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + return -EINVAL; + } + +- /* Read next level */ ++ /* Read next level. */ + err = indx_read(indx, ni, de_get_vbn(e), &n); + if (err) + return err; + +- /* Try next level */ ++ /* Try next level. */ + e = hdr_first_de(&n->index->ihdr); + if (!e) { + kfree(n); +@@ -1250,7 +1250,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni, + if (!de_is_last(e)) + goto next_iter; + +- /* Pop one level */ ++ /* Pop one level. */ + if (n) { + fnd_pop(fnd); + kfree(n); +@@ -1292,35 +1292,35 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + CLST next_vbn; + u32 record_size = ni->mi.sbi->record_size; + +- /* Use non sorted algorithm */ ++ /* Use non sorted algorithm. */ + if (!*entry) { +- /* This is the first call */ ++ /* This is the first call. */ + e = hdr_first_de(&root->ihdr); + if (!e) + return 0; + fnd_clear(fnd); + fnd->root_de = e; + +- /* The first call with setup of initial element */ ++ /* The first call with setup of initial element. */ + if (*off >= record_size) { + next_vbn = (((*off - record_size) >> indx->index_bits)) + << indx->idx2vbn_bits; +- /* jump inside cycle 'for'*/ ++ /* Jump inside cycle 'for'. */ + goto next; + } + +- /* Start enumeration from root */ ++ /* Start enumeration from root. */ + *off = 0; + } else if (!fnd->root_de) + return -EINVAL; + + for (;;) { +- /* Check if current entry can be used */ ++ /* Check if current entry can be used. */ + if (e && le16_to_cpu(e->size) > sizeof(struct NTFS_DE)) + goto ok; + + if (!fnd->level) { +- /* Continue to enumerate root */ ++ /* Continue to enumerate root. */ + if (!de_is_last(fnd->root_de)) { + e = hdr_next_de(&root->ihdr, fnd->root_de); + if (!e) +@@ -1329,10 +1329,10 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + continue; + } + +- /* Start to enumerate indexes from 0 */ ++ /* Start to enumerate indexes from 0. */ + next_vbn = 0; + } else { +- /* Continue to enumerate indexes */ ++ /* Continue to enumerate indexes. */ + e2 = fnd->de[fnd->level - 1]; + + n = fnd->nodes[fnd->level - 1]; +@@ -1345,31 +1345,31 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + continue; + } + +- /* Continue with next index */ ++ /* Continue with next index. */ + next_vbn = le64_to_cpu(n->index->vbn) + + root->index_block_clst; + } + + next: +- /* Release current index */ ++ /* Release current index. */ + if (n) { + fnd_pop(fnd); + put_indx_node(n); + n = NULL; + } + +- /* Skip all free indexes */ ++ /* Skip all free indexes. */ + bit = next_vbn >> indx->idx2vbn_bits; + err = indx_used_bit(indx, ni, &bit); + if (err == -ENOENT || bit == MINUS_ONE_T) { +- /* No used indexes */ ++ /* No used indexes. */ + *entry = NULL; + return 0; + } + + next_used_vbn = bit << indx->idx2vbn_bits; + +- /* Read buffer into memory */ ++ /* Read buffer into memory. */ + err = indx_read(indx, ni, next_used_vbn, &n); + if (err) + return err; +@@ -1381,12 +1381,12 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + ok: +- /* return offset to restore enumerator if necessary */ ++ /* Return offset to restore enumerator if necessary. */ + if (!n) { +- /* 'e' points in root */ ++ /* 'e' points in root, */ + *off = PtrOffset(&root->ihdr, e); + } else { +- /* 'e' points in index */ ++ /* 'e' points in index, */ + *off = (le64_to_cpu(n->index->vbn) << indx->vbn2vbo_bits) + + record_size + PtrOffset(&n->index->ihdr, e); + } +@@ -1396,9 +1396,7 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * indx_create_allocate +- * +- * create "Allocation + Bitmap" attributes ++ * indx_create_allocate - Create "Allocation + Bitmap" attributes. + */ + static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + CLST *vbn) +@@ -1455,9 +1453,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * indx_add_allocate +- * +- * add clusters to index ++ * indx_add_allocate - Add clusters to index. + */ + static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + CLST *vbn) +@@ -1490,7 +1486,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + data_size = (u64)(bit + 1) << indx->index_bits; + + if (bmp) { +- /* Increase bitmap */ ++ /* Increase bitmap. */ + err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len, + &indx->bitmap_run, bitmap_size(bit + 1), + NULL, true, NULL); +@@ -1507,7 +1503,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + goto out1; + } + +- /* Increase allocation */ ++ /* Increase allocation. */ + err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len, + &indx->alloc_run, data_size, &data_size, true, + NULL); +@@ -1522,7 +1518,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + return 0; + + out2: +- /* Ops (no space?) */ ++ /* Ops. No space? */ + attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len, + &indx->bitmap_run, bmp_size, &bmp_size_v, false, NULL); + +@@ -1531,9 +1527,8 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * indx_insert_into_root ++ * indx_insert_into_root - Attempt to insert an entry into the index root. + * +- * attempts to insert an entry into the index root + * If necessary, it will twiddle the index b-tree. + */ + static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, +@@ -1557,7 +1552,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + int ds_root; + struct INDEX_ROOT *root, *a_root; + +- /* Get the record this root placed in */ ++ /* Get the record this root placed in. */ + root = indx_get_root(indx, ni, &attr, &mi); + if (!root) + return -EINVAL; +@@ -1579,7 +1574,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + ds_root = new_de_size + hdr_used - hdr_total; + + if (used + ds_root < sbi->max_bytes_per_attr) { +- /* make a room for new elements */ ++ /* Make a room for new elements. */ + mi_resize_attr(mi, attr, ds_root); + hdr->total = cpu_to_le32(hdr_total + ds_root); + e = hdr_insert_de(indx, hdr, new_de, root_de, ctx); +@@ -1590,16 +1585,19 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + return 0; + } + +- /* Make a copy of root attribute to restore if error */ ++ /* Make a copy of root attribute to restore if error. */ + a_root = kmemdup(attr, asize, GFP_NOFS); + if (!a_root) + return -ENOMEM; + +- /* copy all the non-end entries from the index root to the new buffer.*/ ++ /* ++ * Copy all the non-end entries from ++ * the index root to the new buffer. ++ */ + to_move = 0; + e0 = hdr_first_de(hdr); + +- /* Calculate the size to copy */ ++ /* Calculate the size to copy. */ + for (e = e0;; e = hdr_next_de(hdr, e)) { + if (!e) { + err = -EINVAL; +@@ -1632,7 +1630,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + ds_root = new_root_size - root_size; + + if (ds_root > 0 && used + ds_root > sbi->max_bytes_per_attr) { +- /* make root external */ ++ /* Make root external. */ + err = -EOPNOTSUPP; + goto out_free_re; + } +@@ -1640,7 +1638,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + if (ds_root) + mi_resize_attr(mi, attr, ds_root); + +- /* Fill first entry (vcn will be set later) */ ++ /* Fill first entry (vcn will be set later). */ + e = (struct NTFS_DE *)(root + 1); + memset(e, 0, sizeof(struct NTFS_DE)); + e->size = cpu_to_le16(sizeof(struct NTFS_DE) + sizeof(u64)); +@@ -1653,26 +1651,26 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + fnd->root_de = hdr_first_de(hdr); + mi->dirty = true; + +- /* Create alloc and bitmap attributes (if not) */ ++ /* Create alloc and bitmap attributes (if not). */ + err = run_is_empty(&indx->alloc_run) + ? indx_create_allocate(indx, ni, &new_vbn) + : indx_add_allocate(indx, ni, &new_vbn); + +- /* layout of record may be changed, so rescan root */ ++ /* Layout of record may be changed, so rescan root. */ + root = indx_get_root(indx, ni, &attr, &mi); + if (!root) { +- /* bug? */ ++ /* Bug? */ + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + err = -EINVAL; + goto out_free_re; + } + + if (err) { +- /* restore root */ ++ /* Restore root. */ + if (mi_resize_attr(mi, attr, -ds_root)) + memcpy(attr, a_root, asize); + else { +- /* bug? */ ++ /* Bug? */ + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + } + goto out_free_re; +@@ -1682,7 +1680,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + *(__le64 *)(e + 1) = cpu_to_le64(new_vbn); + mi->dirty = true; + +- /* now we can create/format the new buffer and copy the entries into */ ++ /* Now we can create/format the new buffer and copy the entries into. */ + n = indx_new(indx, ni, new_vbn, sub_vbn); + if (IS_ERR(n)) { + err = PTR_ERR(n); +@@ -1693,19 +1691,19 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + hdr_used = le32_to_cpu(hdr->used); + hdr_total = le32_to_cpu(hdr->total); + +- /* Copy root entries into new buffer */ ++ /* Copy root entries into new buffer. */ + hdr_insert_head(hdr, re, to_move); + +- /* Update bitmap attribute */ ++ /* Update bitmap attribute. */ + indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits); + +- /* Check if we can insert new entry new index buffer */ ++ /* Check if we can insert new entry new index buffer. */ + if (hdr_used + new_de_size > hdr_total) { + /* +- * This occurs if mft record is the same or bigger than index ++ * This occurs if MFT record is the same or bigger than index + * buffer. Move all root new index and have no space to add +- * new entry classic case when mft record is 1K and index +- * buffer 4K the problem should not occurs ++ * new entry classic case when MFT record is 1K and index ++ * buffer 4K the problem should not occurs. + */ + kfree(re); + indx_write(indx, ni, n, 0); +@@ -1717,8 +1715,8 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * Now root is a parent for new index buffer +- * Insert NewEntry a new buffer ++ * Now root is a parent for new index buffer. ++ * Insert NewEntry a new buffer. + */ + e = hdr_insert_de(indx, hdr, new_de, NULL, ctx); + if (!e) { +@@ -1727,7 +1725,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + } + fnd_push(fnd, n, e); + +- /* Just write updates index into disk */ ++ /* Just write updates index into disk. */ + indx_write(indx, ni, n, 0); + + n = NULL; +@@ -1744,7 +1742,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + /* + * indx_insert_into_buffer + * +- * attempts to insert an entry into an Index Allocation Buffer. ++ * Attempt to insert an entry into an Index Allocation Buffer. + * If necessary, it will split the buffer. + */ + static int +@@ -1764,12 +1762,12 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + __le64 t_vbn, *sub_vbn; + u16 sp_size; + +- /* Try the most easy case */ ++ /* Try the most easy case. */ + e = fnd->level - 1 == level ? fnd->de[level] : NULL; + e = hdr_insert_de(indx, hdr1, new_de, e, ctx); + fnd->de[level] = e; + if (e) { +- /* Just write updated index into disk */ ++ /* Just write updated index into disk. */ + indx_write(indx, ni, n1, 0); + return 0; + } +@@ -1809,7 +1807,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + if (err) + goto out; + +- /* Allocate and format memory a new index buffer */ ++ /* Allocate and format memory a new index buffer. */ + n2 = indx_new(indx, ni, new_vbn, sub_vbn); + if (IS_ERR(n2)) { + err = PTR_ERR(n2); +@@ -1818,20 +1816,23 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + + hdr2 = &n2->index->ihdr; + +- /* Make sp a parent for new buffer */ ++ /* Make sp a parent for new buffer. */ + de_set_vbn(up_e, new_vbn); + +- /* copy all the entries <= sp into the new buffer. */ ++ /* Copy all the entries <= sp into the new buffer. */ + de_t = hdr_first_de(hdr1); + to_copy = PtrOffset(de_t, sp); + hdr_insert_head(hdr2, de_t, to_copy); + +- /* remove all entries (sp including) from hdr1 */ ++ /* Remove all entries (sp including) from hdr1. */ + used = le32_to_cpu(hdr1->used) - to_copy - sp_size; + memmove(de_t, Add2Ptr(sp, sp_size), used - le32_to_cpu(hdr1->de_off)); + hdr1->used = cpu_to_le32(used); + +- /* Insert new entry into left or right buffer (depending on sp <=> new_de) */ ++ /* ++ * Insert new entry into left or right buffer ++ * (depending on sp <=> new_de). ++ */ + hdr_insert_de(indx, + (*indx->cmp)(new_de + 1, le16_to_cpu(new_de->key_size), + up_e + 1, le16_to_cpu(up_e->key_size), +@@ -1848,18 +1849,18 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + put_indx_node(n2); + + /* +- * we've finished splitting everybody, so we are ready to ++ * We've finished splitting everybody, so we are ready to + * insert the promoted entry into the parent. + */ + if (!level) { +- /* Insert in root */ ++ /* Insert in root. */ + err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd); + if (err) + goto out; + } else { + /* +- * The target buffer's parent is another index buffer +- * TODO: Remove recursion ++ * The target buffer's parent is another index buffer. ++ * TODO: Remove recursion. + */ + err = indx_insert_into_buffer(indx, ni, root, up_e, ctx, + level - 1, fnd); +@@ -1874,9 +1875,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * indx_insert_entry +- * +- * inserts new entry into index ++ * indx_insert_entry - Insert new entry into index. + */ + int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + const struct NTFS_DE *new_de, const void *ctx, +@@ -1904,7 +1903,10 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + if (fnd_is_empty(fnd)) { +- /* Find the spot the tree where we want to insert the new entry. */ ++ /* ++ * Find the spot the tree where we want to ++ * insert the new entry. ++ */ + err = indx_find(indx, ni, root, new_de + 1, + le16_to_cpu(new_de->key_size), ctx, &diff, &e, + fnd); +@@ -1918,13 +1920,18 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + if (!fnd->level) { +- /* The root is also a leaf, so we'll insert the new entry into it. */ ++ /* ++ * The root is also a leaf, so we'll insert the ++ * new entry into it. ++ */ + err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx, + fnd); + if (err) + goto out; + } else { +- /* found a leaf buffer, so we'll insert the new entry into it.*/ ++ /* ++ * Found a leaf buffer, so we'll insert the new entry into it. ++ */ + err = indx_insert_into_buffer(indx, ni, root, new_de, ctx, + fnd->level - 1, fnd); + if (err) +@@ -1938,9 +1945,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * indx_find_buffer +- * +- * locates a buffer the tree. ++ * indx_find_buffer - Locate a buffer from the tree. + */ + static struct indx_node *indx_find_buffer(struct ntfs_index *indx, + struct ntfs_inode *ni, +@@ -1952,7 +1957,7 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, + struct indx_node *r; + const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; + +- /* Step 1: Scan one level */ ++ /* Step 1: Scan one level. */ + for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { + if (!e) + return ERR_PTR(-EINVAL); +@@ -1964,7 +1969,7 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, + break; + } + +- /* Step2: Do recursion */ ++ /* Step2: Do recursion. */ + e = Add2Ptr(hdr, le32_to_cpu(hdr->de_off)); + for (;;) { + if (de_has_vcn_ex(e)) { +@@ -1987,9 +1992,7 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, + } + + /* +- * indx_shrink +- * +- * deallocates unused tail indexes ++ * indx_shrink - Deallocate unused tail indexes. + */ + static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni, + size_t bit) +@@ -2066,7 +2069,7 @@ static int indx_free_children(struct ntfs_index *indx, struct ntfs_inode *ni, + return err; + + hdr = &n->index->ihdr; +- /* First, recurse into the children, if any.*/ ++ /* First, recurse into the children, if any. */ + if (hdr_has_subnode(hdr)) { + for (e = hdr_first_de(hdr); e; e = hdr_next_de(hdr, e)) { + indx_free_children(indx, ni, e, false); +@@ -2078,7 +2081,9 @@ static int indx_free_children(struct ntfs_index *indx, struct ntfs_inode *ni, + put_indx_node(n); + + i = vbn >> indx->idx2vbn_bits; +- /* We've gotten rid of the children; add this buffer to the free list. */ ++ /* ++ * We've gotten rid of the children; add this buffer to the free list. ++ */ + indx_mark_free(indx, ni, i); + + if (!trim) +@@ -2086,8 +2091,8 @@ static int indx_free_children(struct ntfs_index *indx, struct ntfs_inode *ni, + + /* + * If there are no used indexes after current free index +- * then we can truncate allocation and bitmap +- * Use bitmap to estimate the case ++ * then we can truncate allocation and bitmap. ++ * Use bitmap to estimate the case. + */ + indx_shrink(indx, ni, i + 1); + return 0; +@@ -2096,9 +2101,9 @@ static int indx_free_children(struct ntfs_index *indx, struct ntfs_inode *ni, + /* + * indx_get_entry_to_replace + * +- * finds a replacement entry for a deleted entry +- * always returns a node entry: +- * NTFS_IE_HAS_SUBNODES is set the flags and the size includes the sub_vcn ++ * Find a replacement entry for a deleted entry. ++ * Always returns a node entry: ++ * NTFS_IE_HAS_SUBNODES is set the flags and the size includes the sub_vcn. + */ + static int indx_get_entry_to_replace(struct ntfs_index *indx, + struct ntfs_inode *ni, +@@ -2115,7 +2120,7 @@ static int indx_get_entry_to_replace(struct ntfs_index *indx, + + *de_to_replace = NULL; + +- /* Find first leaf entry down from de_next */ ++ /* Find first leaf entry down from de_next. */ + vbn = de_get_vbn(de_next); + for (;;) { + n = NULL; +@@ -2128,8 +2133,8 @@ static int indx_get_entry_to_replace(struct ntfs_index *indx, + + if (!de_is_last(e)) { + /* +- * This buffer is non-empty, so its first entry could be used as the +- * replacement entry. ++ * This buffer is non-empty, so its first entry ++ * could be used as the replacement entry. + */ + level = fnd->level - 1; + } +@@ -2137,7 +2142,7 @@ static int indx_get_entry_to_replace(struct ntfs_index *indx, + if (!de_has_vcn(e)) + break; + +- /* This buffer is a node. Continue to go down */ ++ /* This buffer is a node. Continue to go down. */ + vbn = de_get_vbn(e); + } + +@@ -2158,15 +2163,16 @@ static int indx_get_entry_to_replace(struct ntfs_index *indx, + + if (!de_has_vcn(re)) { + /* +- * The replacement entry we found doesn't have a sub_vcn. increase its size +- * to hold one. ++ * The replacement entry we found doesn't have a sub_vcn. ++ * increase its size to hold one. + */ + le16_add_cpu(&re->size, sizeof(u64)); + re->flags |= NTFS_IE_HAS_SUBNODES; + } else { + /* +- * The replacement entry we found was a node entry, which means that all +- * its child buffers are empty. Return them to the free pool. ++ * The replacement entry we found was a node entry, which ++ * means that all its child buffers are empty. Return them ++ * to the free pool. + */ + indx_free_children(indx, ni, te, true); + } +@@ -2191,9 +2197,7 @@ static int indx_get_entry_to_replace(struct ntfs_index *indx, + } + + /* +- * indx_delete_entry +- * +- * deletes an entry from the index. ++ * indx_delete_entry - Delete an entry from the index. + */ + int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + const void *key, u32 key_len, const void *ctx) +@@ -2257,13 +2261,13 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + e_size = le16_to_cpu(e->size); + + if (!de_has_vcn_ex(e)) { +- /* The entry to delete is a leaf, so we can just rip it out */ ++ /* The entry to delete is a leaf, so we can just rip it out. */ + hdr_delete_de(hdr, e); + + if (!level) { + hdr->total = hdr->used; + +- /* Shrink resident root attribute */ ++ /* Shrink resident root attribute. */ + mi_resize_attr(mi, attr, 0 - e_size); + goto out; + } +@@ -2306,9 +2310,10 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } else { + /* + * There is no replacement for the current entry. +- * This means that the subtree rooted at its node is empty, +- * and can be deleted, which turn means that the node can +- * just inherit the deleted entry sub_vcn ++ * This means that the subtree rooted at its node ++ * is empty, and can be deleted, which turn means ++ * that the node can just inherit the deleted ++ * entry sub_vcn. + */ + indx_free_children(indx, ni, next, true); + +@@ -2319,17 +2324,17 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } else { + hdr->total = hdr->used; + +- /* Shrink resident root attribute */ ++ /* Shrink resident root attribute. */ + mi_resize_attr(mi, attr, 0 - e_size); + } + } + } + +- /* Delete a branch of tree */ ++ /* Delete a branch of tree. */ + if (!fnd2 || !fnd2->level) + goto out; + +- /* Reinit root 'cause it can be changed */ ++ /* Reinit root 'cause it can be changed. */ + root = indx_get_root(indx, ni, &attr, &mi); + if (!root) { + err = -EINVAL; +@@ -2343,7 +2348,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + + hdr = level ? &fnd->nodes[level - 1]->index->ihdr : &root->ihdr; + +- /* Scan current level */ ++ /* Scan current level. */ + for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { + if (!e) { + err = -EINVAL; +@@ -2360,7 +2365,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + if (!e) { +- /* Do slow search from root */ ++ /* Do slow search from root. */ + struct indx_node *in; + + fnd_clear(fnd); +@@ -2375,7 +2380,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + fnd_push(fnd, in, NULL); + } + +- /* Merge fnd2 -> fnd */ ++ /* Merge fnd2 -> fnd. */ + for (level = 0; level < fnd2->level; level++) { + fnd_push(fnd, fnd2->nodes[level], fnd2->de[level]); + fnd2->nodes[level] = NULL; +@@ -2421,8 +2426,8 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + + if (sub_vbn != de_get_vbn_le(e)) { + /* +- * Didn't find the parent entry, although this buffer is the parent trail. +- * Something is corrupt. ++ * Didn't find the parent entry, although this buffer ++ * is the parent trail. Something is corrupt. + */ + err = -EINVAL; + goto out; +@@ -2430,11 +2435,11 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + + if (de_is_last(e)) { + /* +- * Since we can't remove the end entry, we'll remove its +- * predecessor instead. This means we have to transfer the +- * predecessor's sub_vcn to the end entry. +- * Note: that this index block is not empty, so the +- * predecessor must exist ++ * Since we can't remove the end entry, we'll remove ++ * its predecessor instead. This means we have to ++ * transfer the predecessor's sub_vcn to the end entry. ++ * Note: This index block is not empty, so the ++ * predecessor must exist. + */ + if (!prev) { + err = -EINVAL; +@@ -2452,9 +2457,9 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } + + /* +- * Copy the current entry into a temporary buffer (stripping off its +- * down-pointer, if any) and delete it from the current buffer or root, +- * as appropriate. ++ * Copy the current entry into a temporary buffer (stripping ++ * off its down-pointer, if any) and delete it from the current ++ * buffer or root, as appropriate. + */ + e_size = le16_to_cpu(e->size); + me = kmemdup(e, e_size, GFP_NOFS); +@@ -2474,14 +2479,14 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + level = 0; + hdr->total = hdr->used; + +- /* Shrink resident root attribute */ ++ /* Shrink resident root attribute. */ + mi_resize_attr(mi, attr, 0 - e_size); + } else { + indx_write(indx, ni, n2d, 0); + level = level2; + } + +- /* Mark unused buffers as free */ ++ /* Mark unused buffers as free. */ + trim_bit = -1; + for (; level < fnd->level; level++) { + ib = fnd->nodes[level]->index; +@@ -2512,8 +2517,8 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + } else { + /* + * This tree needs to be collapsed down to an empty root. +- * Recreate the index root as an empty leaf and free all the bits the +- * index allocation bitmap. ++ * Recreate the index root as an empty leaf and free all ++ * the bits the index allocation bitmap. + */ + fnd_clear(fnd); + fnd_clear(fnd2); +@@ -2548,7 +2553,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + goto out; + } + +- /* Fill first entry */ ++ /* Fill first entry. */ + e = (struct NTFS_DE *)(root + 1); + e->ref.low = 0; + e->ref.high = 0; +@@ -2601,7 +2606,7 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, + goto out; + } + +- /* Find entry in directory */ ++ /* Find entry in directory. */ + err = indx_find(indx, ni, root, fname, fname_full_size(fname), sbi, + &diff, &e, fnd); + if (err) +@@ -2620,7 +2625,7 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, + e_fname = (struct ATTR_FILE_NAME *)(e + 1); + + if (!memcmp(&e_fname->dup, dup, sizeof(*dup))) { +- /* nothing to update in index! Try to avoid this call */ ++ /* Nothing to update in index! Try to avoid this call. */ + goto out; + } + +diff --git a/src/inode.c b/src/inode.c +index 520471f35e298cdafc929c1e262409ad91ccb250..b86ec7dd731cea9fd0b41abb40fe4d81ca965c13 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -20,9 +20,7 @@ + #include "ntfs_fs.h" + + /* +- * ntfs_read_mft +- * +- * reads record and parses MFT ++ * ntfs_read_mft - Read record and parses MFT. + */ + static struct inode *ntfs_read_mft(struct inode *inode, + const struct cpu_str *name, +@@ -91,7 +89,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + } + + if (le32_to_cpu(rec->total) != sbi->record_size) { +- // bad inode? ++ // Bad inode? + err = -EINVAL; + goto out; + } +@@ -99,17 +97,17 @@ static struct inode *ntfs_read_mft(struct inode *inode, + if (!is_rec_base(rec)) + goto Ok; + +- /* record should contain $I30 root */ ++ /* Record should contain $I30 root. */ + is_dir = rec->flags & RECORD_FLAG_DIR; + + inode->i_generation = le16_to_cpu(rec->seq); + +- /* Enumerate all struct Attributes MFT */ ++ /* Enumerate all struct Attributes MFT. */ + le = NULL; + attr = NULL; + + /* +- * to reduce tab pressure use goto instead of ++ * To reduce tab pressure use goto instead of + * while( (attr = ni_enum_attr_ex(ni, attr, &le, NULL) )) + */ + next_attr: +@@ -120,7 +118,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + goto end_enum; + + if (le && le->vcn) { +- /* This is non primary attribute segment. Ignore if not MFT */ ++ /* This is non primary attribute segment. Ignore if not MFT. */ + if (ino != MFT_REC_MFT || attr->type != ATTR_DATA) + goto next_attr; + +@@ -190,7 +188,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + + case ATTR_DATA: + if (is_dir) { +- /* ignore data attribute in dir record */ ++ /* Ignore data attribute in dir record. */ + goto next_attr; + } + +@@ -204,7 +202,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + (ino != MFT_REC_SECURE || !attr->non_res || + attr->name_len != ARRAY_SIZE(SDS_NAME) || + memcmp(attr_name(attr), SDS_NAME, sizeof(SDS_NAME))))) { +- /* file contains stream attribute. ignore it */ ++ /* File contains stream attribute. Ignore it. */ + goto next_attr; + } + +@@ -327,10 +325,10 @@ static struct inode *ntfs_read_mft(struct inode *inode, + t32 = le16_to_cpu(attr->nres.run_off); + } + +- /* Looks like normal symlink */ ++ /* Looks like normal symlink. */ + ni->i_valid = inode->i_size; + +- /* Clear directory bit */ ++ /* Clear directory bit. */ + if (ni->ni_flags & NI_FLAG_DIR) { + indx_clear(&ni->dir); + memset(&ni->dir, 0, sizeof(ni->dir)); +@@ -342,7 +340,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + is_dir = false; + if (attr->non_res) { + run = &ni->file.run; +- goto attr_unpack_run; // double break ++ goto attr_unpack_run; // Double break. + } + break; + +@@ -388,7 +386,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + goto out; + + if (!is_match && name) { +- /* reuse rec as buffer for ascii name */ ++ /* Reuse rec as buffer for ascii name. */ + err = -ENOENT; + goto out; + } +@@ -407,9 +405,9 @@ static struct inode *ntfs_read_mft(struct inode *inode, + ni->std_fa |= FILE_ATTRIBUTE_DIRECTORY; + + /* +- * dot and dot-dot should be included in count but was not ++ * Dot and dot-dot should be included in count but was not + * included in enumeration. +- * Usually a hard links to directories are disabled ++ * Usually a hard links to directories are disabled. + */ + inode->i_op = &ntfs_dir_inode_operations; + inode->i_fop = &ntfs_dir_operations; +@@ -433,7 +431,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + init_special_inode(inode, mode, inode->i_rdev); + } else if (fname && fname->home.low == cpu_to_le32(MFT_REC_EXTEND) && + fname->home.seq == cpu_to_le16(MFT_REC_EXTEND)) { +- /* Records in $Extend are not a files or general directories */ ++ /* Records in $Extend are not a files or general directories. */ + } else { + err = -EINVAL; + goto out; +@@ -449,7 +447,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + + inode->i_mode = mode; + if (!(ni->ni_flags & NI_FLAG_EA)) { +- /* if no xattr then no security (stored in xattr) */ ++ /* If no xattr then no security (stored in xattr). */ + inode->i_flags |= S_NOSEC; + } + +@@ -469,7 +467,11 @@ static struct inode *ntfs_read_mft(struct inode *inode, + return ERR_PTR(err); + } + +-/* returns 1 if match */ ++/* ++ * ntfs_test_inode ++ * ++ * Return: 1 if match. ++ */ + static int ntfs_test_inode(struct inode *inode, void *data) + { + struct MFT_REF *ref = data; +@@ -499,7 +501,7 @@ struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, + if (inode->i_state & I_NEW) + inode = ntfs_read_mft(inode, name, ref); + else if (ref->seq != ntfs_i(inode)->mi.mrec->seq) { +- /* inode overlaps? */ ++ /* Inode overlaps? */ + make_bad_inode(inode); + } + +@@ -530,18 +532,18 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, + CLST vcn, lcn, len; + bool new; + +- /*clear previous state*/ ++ /* Clear previous state. */ + clear_buffer_new(bh); + clear_buffer_uptodate(bh); + +- /* direct write uses 'create=0'*/ ++ /* Direct write uses 'create=0'. */ + if (!create && vbo >= ni->i_valid) { +- /* out of valid */ ++ /* Out of valid. */ + return 0; + } + + if (vbo >= inode->i_size) { +- /* out of size */ ++ /* Out of size. */ + return 0; + } + +@@ -593,7 +595,7 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, + valid = ni->i_valid; + + if (ctx == GET_BLOCK_DIRECT_IO_W) { +- /*ntfs_direct_IO will update ni->i_valid */ ++ /* ntfs_direct_IO will update ni->i_valid. */ + if (vbo >= valid) + set_buffer_new(bh); + } else if (create) { +@@ -609,17 +611,17 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, + mark_inode_dirty(inode); + } + } else if (vbo >= valid) { +- /* read out of valid data*/ +- /* should never be here 'cause already checked */ ++ /* Read out of valid data. */ ++ /* Should never be here 'cause already checked. */ + clear_buffer_mapped(bh); + } else if (vbo + bytes <= valid) { +- /* normal read */ ++ /* Normal read. */ + } else if (vbo + block_size <= valid) { +- /* normal short read */ ++ /* Normal short read. */ + bytes = block_size; + } else { + /* +- * read across valid size: vbo < valid && valid < vbo + block_size ++ * Read across valid size: vbo < valid && valid < vbo + block_size + */ + bytes = block_size; + +@@ -700,7 +702,7 @@ static int ntfs_readpage(struct file *file, struct page *page) + return err; + } + +- /* normal + sparse files */ ++ /* Normal + sparse files. */ + return mpage_readpage(page, ntfs_get_block); + } + +@@ -713,12 +715,12 @@ static void ntfs_readahead(struct readahead_control *rac) + loff_t pos; + + if (is_resident(ni)) { +- /* no readahead for resident */ ++ /* No readahead for resident. */ + return; + } + + if (is_compressed(ni)) { +- /* no readahead for compressed */ ++ /* No readahead for compressed. */ + return; + } + +@@ -727,7 +729,7 @@ static void ntfs_readahead(struct readahead_control *rac) + + if (valid < i_size_read(inode) && pos <= valid && + valid < pos + readahead_length(rac)) { +- /* range cross 'valid'. read it page by page */ ++ /* Range cross 'valid'. Read it page by page. */ + return; + } + +@@ -761,7 +763,7 @@ static ssize_t ntfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) + ssize_t ret; + + if (is_resident(ni)) { +- /*switch to buffered write*/ ++ /* Switch to buffered write. */ + ret = 0; + goto out; + } +@@ -781,7 +783,7 @@ static ssize_t ntfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) + mark_inode_dirty(inode); + } + } else if (vbo < valid && valid < end) { +- /* fix page */ ++ /* Fix page. */ + iov_iter_revert(iter, end - valid); + iov_iter_zero(end - valid, iter); + } +@@ -797,7 +799,7 @@ int ntfs_set_size(struct inode *inode, u64 new_size) + struct ntfs_inode *ni = ntfs_i(inode); + int err; + +- /* Check for maximum file size */ ++ /* Check for maximum file size. */ + if (is_sparsed(ni) || is_compressed(ni)) { + if (new_size > sbi->maxbytes_sparse) { + err = -EFBIG; +@@ -848,7 +850,7 @@ static int ntfs_writepages(struct address_space *mapping, + { + struct inode *inode = mapping->host; + struct ntfs_inode *ni = ntfs_i(inode); +- /* redirect call to 'ntfs_writepage' for resident files*/ ++ /* Redirect call to 'ntfs_writepage' for resident files. */ + get_block_t *get_block = is_resident(ni) ? NULL : &ntfs_get_block; + + return mpage_writepages(mapping, wbc, get_block); +@@ -901,7 +903,9 @@ static int ntfs_write_begin(struct file *file, struct address_space *mapping, + return err; + } + +-/* address_space_operations::write_end */ ++/* ++ * ntfs_write_end - Address_space_operations::write_end. ++ */ + static int ntfs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, u32 len, u32 copied, struct page *page, + void *fsdata) +@@ -919,7 +923,7 @@ static int ntfs_write_end(struct file *file, struct address_space *mapping, + ni_unlock(ni); + if (!err) { + dirty = true; +- /* clear any buffers in page*/ ++ /* Clear any buffers in page. */ + if (page_has_buffers(page)) { + struct buffer_head *head, *bh; + +@@ -948,7 +952,7 @@ static int ntfs_write_end(struct file *file, struct address_space *mapping, + } + + if (valid != ni->i_valid) { +- /* ni->i_valid is changed in ntfs_get_block_vbo */ ++ /* ni->i_valid is changed in ntfs_get_block_vbo. */ + dirty = true; + } + +@@ -1009,10 +1013,11 @@ int ntfs_sync_inode(struct inode *inode) + } + + /* +- * helper function for ntfs_flush_inodes. This writes both the inode +- * and the file data blocks, waiting for in flight data blocks before +- * the start of the call. It does not wait for any io started +- * during the call ++ * writeback_inode - Helper function for ntfs_flush_inodes(). ++ * ++ * This writes both the inode and the file data blocks, waiting ++ * for in flight data blocks before the start of the call. It ++ * does not wait for any io started during the call. + */ + static int writeback_inode(struct inode *inode) + { +@@ -1024,12 +1029,14 @@ static int writeback_inode(struct inode *inode) + } + + /* +- * write data and metadata corresponding to i1 and i2. The io is ++ * ntfs_flush_inodes ++ * ++ * Write data and metadata corresponding to i1 and i2. The io is + * started but we do not wait for any of it to finish. + * +- * filemap_flush is used for the block device, so if there is a dirty ++ * filemap_flush() is used for the block device, so if there is a dirty + * page for a block already in flight, we will not wait and start the +- * io over again ++ * io over again. + */ + int ntfs_flush_inodes(struct super_block *sb, struct inode *i1, + struct inode *i2) +@@ -1049,7 +1056,7 @@ int inode_write_data(struct inode *inode, const void *data, size_t bytes) + { + pgoff_t idx; + +- /* Write non resident data */ ++ /* Write non resident data. */ + for (idx = 0; bytes; idx++) { + size_t op = bytes > PAGE_SIZE ? PAGE_SIZE : bytes; + struct page *page = ntfs_map_page(inode->i_mapping, idx); +@@ -1076,12 +1083,14 @@ int inode_write_data(struct inode *inode, const void *data, size_t bytes) + } + + /* +- * number of bytes to for REPARSE_DATA_BUFFER(IO_REPARSE_TAG_SYMLINK) +- * for unicode string of 'uni_len' length ++ * ntfs_reparse_bytes ++ * ++ * Number of bytes to for REPARSE_DATA_BUFFER(IO_REPARSE_TAG_SYMLINK) ++ * for unicode string of @uni_len length. + */ + static inline u32 ntfs_reparse_bytes(u32 uni_len) + { +- /* header + unicode string + decorated unicode string */ ++ /* Header + unicode string + decorated unicode string. */ + return sizeof(short) * (2 * uni_len + 4) + + offsetof(struct REPARSE_DATA_BUFFER, + SymbolicLinkReparseBuffer.PathBuffer); +@@ -1103,14 +1112,14 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname, + rs = &rp->SymbolicLinkReparseBuffer; + rp_name = rs->PathBuffer; + +- /* Convert link name to utf16 */ ++ /* Convert link name to UTF-16. */ + err = ntfs_nls_to_utf16(sbi, symname, size, + (struct cpu_str *)(rp_name - 1), 2 * size, + UTF16_LITTLE_ENDIAN); + if (err < 0) + goto out; + +- /* err = the length of unicode name of symlink */ ++ /* err = the length of unicode name of symlink. */ + *nsize = ntfs_reparse_bytes(err); + + if (*nsize > sbi->reparse.max_size) { +@@ -1118,7 +1127,7 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname, + goto out; + } + +- /* translate linux '/' into windows '\' */ ++ /* Translate Linux '/' into Windows '\'. */ + for (i = 0; i < err; i++) { + if (rp_name[i] == cpu_to_le16('/')) + rp_name[i] = cpu_to_le16('\\'); +@@ -1129,20 +1138,21 @@ ntfs_create_reparse_buffer(struct ntfs_sb_info *sbi, const char *symname, + cpu_to_le16(*nsize - offsetof(struct REPARSE_DATA_BUFFER, + SymbolicLinkReparseBuffer)); + +- /* PrintName + SubstituteName */ ++ /* PrintName + SubstituteName. */ + rs->SubstituteNameOffset = cpu_to_le16(sizeof(short) * err); + rs->SubstituteNameLength = cpu_to_le16(sizeof(short) * err + 8); + rs->PrintNameLength = rs->SubstituteNameOffset; + + /* +- * TODO: use relative path if possible to allow windows to parse this path +- * 0-absolute path 1- relative path (SYMLINK_FLAG_RELATIVE) ++ * TODO: Use relative path if possible to allow Windows to ++ * parse this path. ++ * 0-absolute path 1- relative path (SYMLINK_FLAG_RELATIVE). + */ + rs->Flags = 0; + + memmove(rp_name + err + 4, rp_name, sizeof(short) * err); + +- /* decorate SubstituteName */ ++ /* Decorate SubstituteName. */ + rp_name += err; + rp_name[0] = cpu_to_le16('\\'); + rp_name[1] = cpu_to_le16('?'); +@@ -1204,13 +1214,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + fa = FILE_ATTRIBUTE_REPARSE_POINT; + + /* +- * linux: there are dir/file/symlink and so on +- * NTFS: symlinks are "dir + reparse" or "file + reparse" ++ * linux: there are dir/file/symlink and so on. ++ * NTFS: symlinks are "dir + reparse" or "file + reparse". + * It is good idea to create: + * dir + reparse if 'symname' points to directory + * or + * file + reparse if 'symname' points to file +- * Unfortunately kern_path hangs if symname contains 'dir' ++ * Unfortunately kern_path hangs if symname contains 'dir'. + */ + + /* +@@ -1229,14 +1239,14 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + */ + } else if (S_ISREG(mode)) { + if (sbi->options.sparse) { +- /* sparsed regular file, cause option 'sparse' */ ++ /* Sparsed regular file, cause option 'sparse'. */ + fa = FILE_ATTRIBUTE_SPARSE_FILE | + FILE_ATTRIBUTE_ARCHIVE; + } else if (dir_ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) { +- /* compressed regular file, if parent is compressed */ ++ /* Compressed regular file, if parent is compressed. */ + fa = FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ARCHIVE; + } else { +- /* regular file, default attributes */ ++ /* Regular file, default attributes. */ + fa = FILE_ATTRIBUTE_ARCHIVE; + } + } else { +@@ -1246,17 +1256,17 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + if (!(mode & 0222)) + fa |= FILE_ATTRIBUTE_READONLY; + +- /* allocate PATH_MAX bytes */ ++ /* Allocate PATH_MAX bytes. */ + new_de = __getname(); + if (!new_de) { + err = -ENOMEM; + goto out1; + } + +- /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ /* Mark rw ntfs as dirty. it will be cleared at umount. */ + ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); + +- /* Step 1: allocate and fill new mft record */ ++ /* Step 1: allocate and fill new mft record. */ + err = ntfs_look_free_mft(sbi, &ino, false, NULL, NULL); + if (err) + goto out2; +@@ -1277,7 +1287,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + rec->hard_links = cpu_to_le16(1); + attr = Add2Ptr(rec, le16_to_cpu(rec->attr_off)); + +- /* Get default security id */ ++ /* Get default security id. */ + sd = s_default_security; + sd_size = sizeof(s_default_security); + +@@ -1293,7 +1303,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + } + } + +- /* Insert standard info */ ++ /* Insert standard info. */ + std5 = Add2Ptr(attr, SIZEOF_RESIDENT); + + if (security_id == SECURITY_ID_INVALID) { +@@ -1319,7 +1329,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + attr = Add2Ptr(attr, asize); + +- /* Insert file name */ ++ /* Insert file name. */ + err = fill_name_de(sbi, new_de, name, uni); + if (err) + goto out4; +@@ -1348,7 +1358,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + attr = Add2Ptr(attr, asize); + + if (security_id == SECURITY_ID_INVALID) { +- /* Insert security attribute */ ++ /* Insert security attribute. */ + asize = SIZEOF_RESIDENT + ALIGN(sd_size, 8); + + attr->type = ATTR_SECURE; +@@ -1363,8 +1373,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + if (fa & FILE_ATTRIBUTE_DIRECTORY) { + /* +- * regular directory or symlink to directory +- * Create root attribute ++ * Regular directory or symlink to directory. ++ * Create root attribute. + */ + dsize = sizeof(struct INDEX_ROOT) + sizeof(struct NTFS_DE); + asize = sizeof(I30_NAME) + SIZEOF_RESIDENT + dsize; +@@ -1394,12 +1404,12 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + e->flags = NTFS_IE_LAST; + } else if (S_ISLNK(mode)) { + /* +- * symlink to file +- * Create empty resident data attribute ++ * Symlink to file. ++ * Create empty resident data attribute. + */ + asize = SIZEOF_RESIDENT; + +- /* insert empty ATTR_DATA */ ++ /* Insert empty ATTR_DATA */ + attr->type = ATTR_DATA; + attr->size = cpu_to_le32(SIZEOF_RESIDENT); + attr->id = cpu_to_le16(aid++); +@@ -1407,13 +1417,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + attr->res.data_off = SIZEOF_RESIDENT_LE; + } else { + /* +- * regular file or node ++ * Regular file or node. + */ + attr->type = ATTR_DATA; + attr->id = cpu_to_le16(aid++); + + if (S_ISREG(mode)) { +- /* Create empty non resident data attribute */ ++ /* Create empty non resident data attribute. */ + attr->non_res = 1; + attr->nres.evcn = cpu_to_le64(-1ll); + if (fa & FILE_ATTRIBUTE_SPARSE_FILE) { +@@ -1437,7 +1447,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + } + attr->nres.run_off = attr->name_off; + } else { +- /* Create empty resident data attribute */ ++ /* Create empty resident data attribute. */ + attr->size = cpu_to_le32(SIZEOF_RESIDENT); + attr->name_off = SIZEOF_RESIDENT_LE; + if (fa & FILE_ATTRIBUTE_SPARSE_FILE) +@@ -1465,13 +1475,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + } + + /* +- * Insert ATTR_REPARSE ++ * Insert ATTR_REPARSE. + */ + attr = Add2Ptr(attr, asize); + attr->type = ATTR_REPARSE; + attr->id = cpu_to_le16(aid++); + +- /* resident or non resident? */ ++ /* Resident or non resident? */ + asize = ALIGN(SIZEOF_RESIDENT + nsize, 8); + t16 = PtrOffset(rec, attr); + +@@ -1479,7 +1489,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + CLST alen; + CLST clst = bytes_to_cluster(sbi, nsize); + +- /* bytes per runs */ ++ /* Bytes per runs. */ + t16 = sbi->record_size - t16 - SIZEOF_NONRESIDENT; + + attr->non_res = 1; +@@ -1534,12 +1544,12 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + rec->used = cpu_to_le32(PtrOffset(rec, attr) + 8); + rec->next_attr_id = cpu_to_le16(aid); + +- /* Step 2: Add new name in index */ ++ /* Step 2: Add new name in index. */ + err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd); + if (err) + goto out6; + +- /* Update current directory record */ ++ /* Update current directory record. */ + mark_inode_dirty(dir); + + inode->i_generation = le16_to_cpu(rec->seq); +@@ -1577,26 +1587,29 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + inode->i_flags |= S_NOSEC; + } + +- /* Write non resident data */ ++ /* Write non resident data. */ + if (nsize) { + err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rp, nsize); + if (err) + goto out7; + } + +- /* call 'd_instantiate' after inode->i_op is set but before finish_open */ ++ /* ++ * Call 'd_instantiate' after inode->i_op is set ++ * but before finish_open. ++ */ + d_instantiate(dentry, inode); + + ntfs_save_wsl_perm(inode); + mark_inode_dirty(inode); + mark_inode_dirty(dir); + +- /* normal exit */ ++ /* Normal exit. */ + goto out2; + + out7: + +- /* undo 'indx_insert_entry' */ ++ /* Undo 'indx_insert_entry'. */ + indx_delete_entry(&dir_ni->dir, dir_ni, new_de + 1, + le16_to_cpu(new_de->key_size), sbi); + out6: +@@ -1649,15 +1662,15 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) + if (!dir_root) + return -EINVAL; + +- /* allocate PATH_MAX bytes */ ++ /* Allocate PATH_MAX bytes. */ + new_de = __getname(); + if (!new_de) + return -ENOMEM; + +- /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ /* Mark rw ntfs as dirty. It will be cleared at umount. */ + ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY); + +- // Insert file name ++ /* Insert file name. */ + err = fill_name_de(sbi, new_de, name, NULL); + if (err) + goto out; +@@ -1731,23 +1744,23 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) + goto out1; + } + +- /* allocate PATH_MAX bytes */ ++ /* Allocate PATH_MAX bytes. */ + uni = __getname(); + if (!uni) { + err = -ENOMEM; + goto out1; + } + +- /* Convert input string to unicode */ ++ /* Convert input string to unicode. */ + err = ntfs_nls_to_utf16(sbi, name->name, name->len, uni, NTFS_NAME_LEN, + UTF16_HOST_ENDIAN); + if (err < 0) + goto out2; + +- /*mark rw ntfs as dirty. it will be cleared at umount*/ ++ /* Mark rw ntfs as dirty. It will be cleared at umount. */ + ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); + +- /* find name in record */ ++ /* Find name in record. */ + mi_get_ref(&dir_ni->mi, &ref); + + le = NULL; +@@ -1764,14 +1777,14 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) + if (err) + goto out3; + +- /* Then remove name from mft */ ++ /* Then remove name from MFT. */ + ni_remove_attr_le(ni, attr_from_name(fname), le); + + le16_add_cpu(&ni->mi.mrec->hard_links, -1); + ni->mi.dirty = true; + + if (name_type != FILE_NAME_POSIX) { +- /* Now we should delete name by type */ ++ /* Now we should delete name by type. */ + fname = ni_fname_type(ni, name_type, &le); + if (fname) { + err = indx_delete_entry(indx, dir_ni, fname, +@@ -1837,13 +1850,13 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + struct le_str *uni; + struct ATTRIB *attr; + +- /* Reparse data present. Try to parse it */ ++ /* Reparse data present. Try to parse it. */ + static_assert(!offsetof(struct REPARSE_DATA_BUFFER, ReparseTag)); + static_assert(sizeof(u32) == sizeof(rp->ReparseTag)); + + *buffer = 0; + +- /* Read into temporal buffer */ ++ /* Read into temporal buffer. */ + if (i_size > sbi->reparse.max_size || i_size <= sizeof(u32)) { + err = -EINVAL; + goto out; +@@ -1875,10 +1888,10 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + + err = -EINVAL; + +- /* Microsoft Tag */ ++ /* Microsoft Tag. */ + switch (rp->ReparseTag) { + case IO_REPARSE_TAG_MOUNT_POINT: +- /* Mount points and junctions */ ++ /* Mount points and junctions. */ + /* Can we use 'Rp->MountPointReparseBuffer.PrintNameLength'? */ + if (i_size <= offsetof(struct REPARSE_DATA_BUFFER, + MountPointReparseBuffer.PathBuffer)) +@@ -1940,20 +1953,20 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + goto out; + } + +- /* Users tag */ ++ /* Users tag. */ + uni = Add2Ptr(rp, sizeof(struct REPARSE_POINT) - 2); + nlen = le16_to_cpu(rp->ReparseDataLength) - + sizeof(struct REPARSE_POINT); + } + +- /* Convert nlen from bytes to UNICODE chars */ ++ /* Convert nlen from bytes to UNICODE chars. */ + nlen >>= 1; + +- /* Check that name is available */ ++ /* Check that name is available. */ + if (!nlen || &uni->name[nlen] > (__le16 *)Add2Ptr(rp, i_size)) + goto out; + +- /* If name is already zero terminated then truncate it now */ ++ /* If name is already zero terminated then truncate it now. */ + if (!uni->name[nlen - 1]) + nlen -= 1; + uni->len = nlen; +@@ -1963,13 +1976,13 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + if (err < 0) + goto out; + +- /* translate windows '\' into linux '/' */ ++ /* Translate Windows '\' into Linux '/'. */ + for (i = 0; i < err; i++) { + if (buffer[i] == '\\') + buffer[i] = '/'; + } + +- /* Always set last zero */ ++ /* Always set last zero. */ + buffer[err] = 0; + out: + kfree(to_free); +diff --git a/src/lznt.c b/src/lznt.c +index eb43b9f5149b0d0e6364c6226b426f61d7e651de..3acf0d9f0b1511d967a6613e05c5845046b97549 100644 +--- a/src/lznt.c ++++ b/src/lznt.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * + */ ++ + #include + #include + #include +@@ -14,7 +15,7 @@ + #include "ntfs_fs.h" + + // clang-format off +-/* src buffer is zero */ ++/* Src buffer is zero. */ + #define LZNT_ERROR_ALL_ZEROS 1 + #define LZNT_CHUNK_SIZE 0x1000 + // clang-format on +@@ -72,7 +73,7 @@ static size_t longest_match_std(const u8 *src, struct lznt *ctx) + hash[1] + 3, ctx->max_len - 3); + } + +- /* Compare two matches and select the best one */ ++ /* Compare two matches and select the best one. */ + if (len1 < len2) { + ctx->best_match = hash[1]; + len1 = len2; +@@ -129,10 +130,10 @@ static inline size_t parse_pair(u16 pair, size_t *offset, size_t index) + /* + * compress_chunk + * +- * returns one of the three values: +- * 0 - ok, 'cmpr' contains 'cmpr_chunk_size' bytes of compressed data +- * 1 - input buffer is full zero +- * -2 - the compressed buffer is too small to hold the compressed data ++ * Return: ++ * * 0 - Ok, @cmpr contains @cmpr_chunk_size bytes of compressed data. ++ * * 1 - Input buffer is full zero. ++ * * -2 - The compressed buffer is too small to hold the compressed data. + */ + static inline int compress_chunk(size_t (*match)(const u8 *, struct lznt *), + const u8 *unc, const u8 *unc_end, u8 *cmpr, +@@ -145,7 +146,7 @@ static inline int compress_chunk(size_t (*match)(const u8 *, struct lznt *), + u8 *cp = cmpr + 3; + u8 *cp2 = cmpr + 2; + u8 not_zero = 0; +- /* Control byte of 8-bit values: ( 0 - means byte as is, 1 - short pair ) */ ++ /* Control byte of 8-bit values: ( 0 - means byte as is, 1 - short pair ). */ + u8 ohdr = 0; + u8 *last; + u16 t16; +@@ -165,7 +166,7 @@ static inline int compress_chunk(size_t (*match)(const u8 *, struct lznt *), + while (unc + s_max_off[idx] < up) + ctx->max_len = s_max_len[++idx]; + +- // Find match ++ /* Find match. */ + max_len = up + 3 <= unc_end ? (*match)(up, ctx) : 0; + + if (!max_len) { +@@ -211,7 +212,7 @@ static inline int compress_chunk(size_t (*match)(const u8 *, struct lznt *), + return -2; + + /* +- * Copy non cmpr data ++ * Copy non cmpr data. + * 0x3FFF == ((LZNT_CHUNK_SIZE + 2 - 3) | 0x3000) + */ + cmpr[0] = 0xff; +@@ -233,38 +234,38 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, + u16 pair; + size_t offset, length; + +- /* Do decompression until pointers are inside range */ ++ /* Do decompression until pointers are inside range. */ + while (up < unc_end && cmpr < cmpr_end) { + /* Correct index */ + while (unc + s_max_off[index] < up) + index += 1; + +- /* Check the current flag for zero */ ++ /* Check the current flag for zero. */ + if (!(ch & (1 << bit))) { +- /* Just copy byte */ ++ /* Just copy byte. */ + *up++ = *cmpr++; + goto next; + } + +- /* Check for boundary */ ++ /* Check for boundary. */ + if (cmpr + 1 >= cmpr_end) + return -EINVAL; + +- /* Read a short from little endian stream */ ++ /* Read a short from little endian stream. */ + pair = cmpr[1]; + pair <<= 8; + pair |= cmpr[0]; + + cmpr += 2; + +- /* Translate packed information into offset and length */ ++ /* Translate packed information into offset and length. */ + length = parse_pair(pair, &offset, index); + +- /* Check offset for boundary */ ++ /* Check offset for boundary. */ + if (unc + offset > up) + return -EINVAL; + +- /* Truncate the length if necessary */ ++ /* Truncate the length if necessary. */ + if (up + length >= unc_end) + length = unc_end - up; + +@@ -273,7 +274,7 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, + *up = *(up - offset); + + next: +- /* Advance flag bit value */ ++ /* Advance flag bit value. */ + bit = (bit + 1) & 7; + + if (!bit) { +@@ -284,13 +285,14 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, + } + } + +- /* return the size of uncompressed data */ ++ /* Return the size of uncompressed data. */ + return up - unc; + } + + /* +- * 0 - standard compression +- * !0 - best compression, requires a lot of cpu ++ * get_lznt_ctx ++ * @level: 0 - Standard compression. ++ * !0 - Best compression, requires a lot of cpu. + */ + struct lznt *get_lznt_ctx(int level) + { +@@ -303,11 +305,11 @@ struct lznt *get_lznt_ctx(int level) + } + + /* +- * compress_lznt ++ * compress_lznt - Compresses @unc into @cmpr + * +- * Compresses "unc" into "cmpr" +- * +x - ok, 'cmpr' contains 'final_compressed_size' bytes of compressed data +- * 0 - input buffer is full zero ++ * Return: ++ * * +x - Ok, @cmpr contains 'final_compressed_size' bytes of compressed data. ++ * * 0 - Input buffer is full zero. + */ + size_t compress_lznt(const void *unc, size_t unc_size, void *cmpr, + size_t cmpr_size, struct lznt *ctx) +@@ -327,7 +329,7 @@ size_t compress_lznt(const void *unc, size_t unc_size, void *cmpr, + match = &longest_match_best; + } + +- /* compression cycle */ ++ /* Compression cycle. */ + for (; unc_chunk < unc_end; unc_chunk += LZNT_CHUNK_SIZE) { + cmpr_size = 0; + err = compress_chunk(match, unc_chunk, unc_end, p, end, +@@ -348,9 +350,7 @@ size_t compress_lznt(const void *unc, size_t unc_size, void *cmpr, + } + + /* +- * decompress_lznt +- * +- * decompresses "cmpr" into "unc" ++ * decompress_lznt - Decompress @cmpr into @unc. + */ + ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, + size_t unc_size) +@@ -364,24 +364,24 @@ ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, + if (cmpr_size < sizeof(short)) + return -EINVAL; + +- /* read chunk header */ ++ /* Read chunk header. */ + chunk_hdr = cmpr_chunk[1]; + chunk_hdr <<= 8; + chunk_hdr |= cmpr_chunk[0]; + +- /* loop through decompressing chunks */ ++ /* Loop through decompressing chunks. */ + for (;;) { + size_t chunk_size_saved; + size_t unc_use; + size_t cmpr_use = 3 + (chunk_hdr & (LZNT_CHUNK_SIZE - 1)); + +- /* Check that the chunk actually fits the supplied buffer */ ++ /* Check that the chunk actually fits the supplied buffer. */ + if (cmpr_chunk + cmpr_use > cmpr_end) + return -EINVAL; + +- /* First make sure the chunk contains compressed data */ ++ /* First make sure the chunk contains compressed data. */ + if (chunk_hdr & 0x8000) { +- /* Decompress a chunk and return if we get an error */ ++ /* Decompress a chunk and return if we get an error. */ + ssize_t err = + decompress_chunk(unc_chunk, unc_end, + cmpr_chunk + sizeof(chunk_hdr), +@@ -390,7 +390,7 @@ ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, + return err; + unc_use = err; + } else { +- /* This chunk does not contain compressed data */ ++ /* This chunk does not contain compressed data. */ + unc_use = unc_chunk + LZNT_CHUNK_SIZE > unc_end + ? unc_end - unc_chunk + : LZNT_CHUNK_SIZE; +@@ -404,21 +404,21 @@ ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, + unc_use); + } + +- /* Advance pointers */ ++ /* Advance pointers. */ + cmpr_chunk += cmpr_use; + unc_chunk += unc_use; + +- /* Check for the end of unc buffer */ ++ /* Check for the end of unc buffer. */ + if (unc_chunk >= unc_end) + break; + +- /* Proceed the next chunk */ ++ /* Proceed the next chunk. */ + if (cmpr_chunk > cmpr_end - 2) + break; + + chunk_size_saved = LZNT_CHUNK_SIZE; + +- /* read chunk header */ ++ /* Read chunk header. */ + chunk_hdr = cmpr_chunk[1]; + chunk_hdr <<= 8; + chunk_hdr |= cmpr_chunk[0]; +@@ -426,12 +426,12 @@ ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, + if (!chunk_hdr) + break; + +- /* Check the size of unc buffer */ ++ /* Check the size of unc buffer. */ + if (unc_use < chunk_size_saved) { + size_t t1 = chunk_size_saved - unc_use; + u8 *t2 = unc_chunk + t1; + +- /* 'Zero' memory */ ++ /* 'Zero' memory. */ + if (t2 >= unc_end) + break; + +@@ -440,13 +440,13 @@ ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc, + } + } + +- /* Check compression boundary */ ++ /* Check compression boundary. */ + if (cmpr_chunk > cmpr_end) + return -EINVAL; + + /* + * The unc size is just a difference between current +- * pointer and original one ++ * pointer and original one. + */ + return PtrOffset(unc, unc_chunk); + } +diff --git a/src/namei.c b/src/namei.c +index 0626844e6bdc72ccd21d839175205875d4b52f63..f79a399bd015056f320714c91b935afac16eb488 100644 +--- a/src/namei.c ++++ b/src/namei.c +@@ -17,9 +17,7 @@ + #include "ntfs_fs.h" + + /* +- * fill_name_de +- * +- * formats NTFS_DE in 'buf' ++ * fill_name_de - Format NTFS_DE in @buf. + */ + int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, + const struct cpu_str *uni) +@@ -46,7 +44,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, + fname->name_len = uni->len; + + } else { +- /* Convert input string to unicode */ ++ /* Convert input string to unicode. */ + err = ntfs_nls_to_utf16(sbi, name->name, name->len, + (struct cpu_str *)&fname->name_len, + NTFS_NAME_LEN, UTF16_LITTLE_ENDIAN); +@@ -66,9 +64,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, + } + + /* +- * ntfs_lookup +- * +- * inode_operations::lookup ++ * ntfs_lookup - inode_operations::lookup + */ + static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, + u32 flags) +@@ -98,9 +94,7 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, + } + + /* +- * ntfs_create +- * +- * inode_operations::create ++ * ntfs_create - inode_operations::create + */ + static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) +@@ -140,9 +134,7 @@ static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, + } + + /* +- * ntfs_link +- * +- * inode_operations::link ++ * ntfs_link - inode_operations::link + */ + static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) + { +@@ -182,9 +174,7 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) + } + + /* +- * ntfs_unlink +- * +- * inode_operations::unlink ++ * ntfs_unlink - inode_operations::unlink + */ + static int ntfs_unlink(struct inode *dir, struct dentry *dentry) + { +@@ -201,9 +191,7 @@ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) + } + + /* +- * ntfs_symlink +- * +- * inode_operations::symlink ++ * ntfs_symlink - inode_operations::symlink + */ + static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, const char *symname) +@@ -223,9 +211,7 @@ static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, + } + + /* +- * ntfs_mkdir +- * +- * inode_operations::mkdir ++ * ntfs_mkdir- inode_operations::mkdir + */ + static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode) +@@ -244,9 +230,7 @@ static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, + } + + /* +- * ntfs_rmdir +- * +- * inode_operations::rm_dir ++ * ntfs_rmdir - inode_operations::rm_dir + */ + static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) + { +@@ -263,9 +247,7 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) + } + + /* +- * ntfs_rename +- * +- * inode_operations::rename ++ * ntfs_rename - inode_operations::rename + */ + static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, +@@ -304,7 +286,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + old_dentry->d_name.len); + + if (is_same && old_dir == new_dir) { +- /* Nothing to do */ ++ /* Nothing to do. */ + err = 0; + goto out; + } +@@ -315,7 +297,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + } + + if (new_inode) { +- /*target name exists. unlink it*/ ++ /* Target name exists. Unlink it. */ + dget(new_dentry); + ni_lock_dir(new_dir_ni); + err = ntfs_unlink_inode(new_dir, new_dentry); +@@ -325,7 +307,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + goto out; + } + +- /* allocate PATH_MAX bytes */ ++ /* Allocate PATH_MAX bytes. */ + old_de = __getname(); + if (!old_de) { + err = -ENOMEM; +@@ -352,7 +334,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + + mi_get_ref(&old_dir_ni->mi, &old_name->home); + +- /*get pointer to file_name in mft*/ ++ /* Get pointer to file_name in MFT. */ + fname = ni_fname_name(old_ni, (struct cpu_str *)&old_name->name_len, + &old_name->home, &le); + if (!fname) { +@@ -360,19 +342,19 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + goto out2; + } + +- /* Copy fname info from record into new fname */ ++ /* Copy fname info from record into new fname. */ + new_name = (struct ATTR_FILE_NAME *)(new_de + 1); + memcpy(&new_name->dup, &fname->dup, sizeof(fname->dup)); + + name_type = paired_name(fname->type); + +- /* remove first name from directory */ ++ /* Remove first name from directory. */ + err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1, + le16_to_cpu(old_de->key_size), sbi); + if (err) + goto out3; + +- /* remove first name from mft */ ++ /* Remove first name from MFT. */ + err = ni_remove_attr_le(old_ni, attr_from_name(fname), le); + if (err) + goto out4; +@@ -381,17 +363,17 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + old_ni->mi.dirty = true; + + if (name_type != FILE_NAME_POSIX) { +- /* get paired name */ ++ /* Get paired name. */ + fname = ni_fname_type(old_ni, name_type, &le); + if (fname) { +- /* remove second name from directory */ ++ /* Remove second name from directory. */ + err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, + fname, fname_full_size(fname), + sbi); + if (err) + goto out5; + +- /* remove second name from mft */ ++ /* Remove second name from MFT. */ + err = ni_remove_attr_le(old_ni, attr_from_name(fname), + le); + if (err) +@@ -402,13 +384,13 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + } + } + +- /* Add new name */ ++ /* Add new name. */ + mi_get_ref(&old_ni->mi, &new_de->ref); + mi_get_ref(&ntfs_i(new_dir)->mi, &new_name->home); + + new_de_key_size = le16_to_cpu(new_de->key_size); + +- /* insert new name in mft */ ++ /* Insert new name in MFT. */ + err = ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0, + &attr, NULL); + if (err) +@@ -421,7 +403,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + le16_add_cpu(&old_ni->mi.mrec->hard_links, 1); + old_ni->mi.dirty = true; + +- /* insert new name in directory */ ++ /* Insert new name in directory. */ + err = indx_insert_entry(&new_dir_ni->dir, new_dir_ni, new_de, sbi, + NULL); + if (err) +@@ -449,7 +431,7 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + } + + err = 0; +- /* normal way */ ++ /* Normal way* */ + goto out2; + + out8: +diff --git a/src/ntfs.h b/src/ntfs.h +index f6d6f1bd312e75ac5c5a61c9baa3d4cb7c5c9f19..0fd7bffb98d4dfe030ee2b4eaf9fa75647f8369d 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -10,33 +10,24 @@ + #ifndef _LINUX_NTFS3_NTFS_H + #define _LINUX_NTFS3_NTFS_H + +-/* TODO: +- * - Check 4K mft record and 512 bytes cluster +- */ ++/* TODO: Check 4K MFT record and 512 bytes cluster. */ + +-/* +- * Activate this define to use binary search in indexes +- */ ++/* Activate this define to use binary search in indexes. */ + #define NTFS3_INDEX_BINARY_SEARCH + +-/* +- * Check each run for marked clusters +- */ ++/* Check each run for marked clusters. */ + #define NTFS3_CHECK_FREE_CLST + + #define NTFS_NAME_LEN 255 + +-/* +- * ntfs.sys used 500 maximum links +- * on-disk struct allows up to 0xffff +- */ ++/* ntfs.sys used 500 maximum links on-disk struct allows up to 0xffff. */ + #define NTFS_LINK_MAX 0x400 + //#define NTFS_LINK_MAX 0xffff + + /* +- * Activate to use 64 bit clusters instead of 32 bits in ntfs.sys +- * Logical and virtual cluster number +- * If needed, may be redefined to use 64 bit value ++ * Activate to use 64 bit clusters instead of 32 bits in ntfs.sys. ++ * Logical and virtual cluster number if needed, may be ++ * redefined to use 64 bit value. + */ + //#define CONFIG_NTFS3_64BIT_CLUSTER + +@@ -52,10 +43,10 @@ struct GUID { + }; + + /* +- * this struct repeats layout of ATTR_FILE_NAME +- * at offset 0x40 +- * it used to store global constants NAME_MFT/NAME_MIRROR... +- * most constant names are shorter than 10 ++ * This struct repeats layout of ATTR_FILE_NAME ++ * at offset 0x40. ++ * It used to store global constants NAME_MFT/NAME_MIRROR... ++ * most constant names are shorter than 10. + */ + struct cpu_str { + u8 len; +@@ -178,11 +169,11 @@ extern const __le16 BAD_NAME[4]; + extern const __le16 SDS_NAME[4]; + extern const __le16 WOF_NAME[17]; /* WofCompressedData */ + +-/* MFT record number structure */ ++/* MFT record number structure. */ + struct MFT_REF { +- __le32 low; // The low part of the number +- __le16 high; // The high part of the number +- __le16 seq; // The sequence number of MFT record ++ __le32 low; // The low part of the number. ++ __le16 high; // The high part of the number. ++ __le16 seq; // The sequence number of MFT record. + }; + + static_assert(sizeof(__le64) == sizeof(struct MFT_REF)); +@@ -197,36 +188,36 @@ static inline CLST ino_get(const struct MFT_REF *ref) + } + + struct NTFS_BOOT { +- u8 jump_code[3]; // 0x00: Jump to boot code ++ u8 jump_code[3]; // 0x00: Jump to boot code. + u8 system_id[8]; // 0x03: System ID, equals "NTFS " + +- // NOTE: this member is not aligned(!) +- // bytes_per_sector[0] must be 0 +- // bytes_per_sector[1] must be multiplied by 256 +- u8 bytes_per_sector[2]; // 0x0B: Bytes per sector ++ // NOTE: This member is not aligned(!) ++ // bytes_per_sector[0] must be 0. ++ // bytes_per_sector[1] must be multiplied by 256. ++ u8 bytes_per_sector[2]; // 0x0B: Bytes per sector. + +- u8 sectors_per_clusters;// 0x0D: Sectors per cluster ++ u8 sectors_per_clusters;// 0x0D: Sectors per cluster. + u8 unused1[7]; + u8 media_type; // 0x15: Media type (0xF8 - harddisk) + u8 unused2[2]; +- __le16 sct_per_track; // 0x18: number of sectors per track +- __le16 heads; // 0x1A: number of heads per cylinder +- __le32 hidden_sectors; // 0x1C: number of 'hidden' sectors ++ __le16 sct_per_track; // 0x18: number of sectors per track. ++ __le16 heads; // 0x1A: number of heads per cylinder. ++ __le32 hidden_sectors; // 0x1C: number of 'hidden' sectors. + u8 unused3[4]; +- u8 bios_drive_num; // 0x24: BIOS drive number =0x80 ++ u8 bios_drive_num; // 0x24: BIOS drive number =0x80. + u8 unused4; +- u8 signature_ex; // 0x26: Extended BOOT signature =0x80 ++ u8 signature_ex; // 0x26: Extended BOOT signature =0x80. + u8 unused5; +- __le64 sectors_per_volume;// 0x28: size of volume in sectors +- __le64 mft_clst; // 0x30: first cluster of $MFT +- __le64 mft2_clst; // 0x38: first cluster of $MFTMirr +- s8 record_size; // 0x40: size of MFT record in clusters(sectors) ++ __le64 sectors_per_volume;// 0x28: Size of volume in sectors. ++ __le64 mft_clst; // 0x30: First cluster of $MFT ++ __le64 mft2_clst; // 0x38: First cluster of $MFTMirr ++ s8 record_size; // 0x40: Size of MFT record in clusters(sectors). + u8 unused6[3]; +- s8 index_size; // 0x44: size of INDX record in clusters(sectors) ++ s8 index_size; // 0x44: Size of INDX record in clusters(sectors). + u8 unused7[3]; + __le64 serial_num; // 0x48: Volume serial number + __le32 check_sum; // 0x50: Simple additive checksum of all +- // of the u32's which precede the 'check_sum' ++ // of the u32's which precede the 'check_sum'. + + u8 boot_code[0x200 - 0x50 - 2 - 4]; // 0x54: + u8 boot_magic[2]; // 0x1FE: Boot signature =0x55 + 0xAA +@@ -247,13 +238,13 @@ enum NTFS_SIGNATURE { + + static_assert(sizeof(enum NTFS_SIGNATURE) == 4); + +-/* MFT Record header structure */ ++/* MFT Record header structure. */ + struct NTFS_RECORD_HEADER { +- /* Record magic number, equals 'FILE'/'INDX'/'RSTR'/'RCRD' */ ++ /* Record magic number, equals 'FILE'/'INDX'/'RSTR'/'RCRD'. */ + enum NTFS_SIGNATURE sign; // 0x00: + __le16 fix_off; // 0x04: + __le16 fix_num; // 0x06: +- __le64 lsn; // 0x08: Log file sequence number ++ __le64 lsn; // 0x08: Log file sequence number, + }; + + static_assert(sizeof(struct NTFS_RECORD_HEADER) == 0x10); +@@ -263,7 +254,7 @@ static inline int is_baad(const struct NTFS_RECORD_HEADER *hdr) + return hdr->sign == NTFS_BAAD_SIGNATURE; + } + +-/* Possible bits in struct MFT_REC.flags */ ++/* Possible bits in struct MFT_REC.flags. */ + enum RECORD_FLAG { + RECORD_FLAG_IN_USE = cpu_to_le16(0x0001), + RECORD_FLAG_DIR = cpu_to_le16(0x0002), +@@ -271,22 +262,22 @@ enum RECORD_FLAG { + RECORD_FLAG_UNKNOWN = cpu_to_le16(0x0008), + }; + +-/* MFT Record structure */ ++/* MFT Record structure, */ + struct MFT_REC { + struct NTFS_RECORD_HEADER rhdr; // 'FILE' + +- __le16 seq; // 0x10: Sequence number for this record +- __le16 hard_links; // 0x12: The number of hard links to record +- __le16 attr_off; // 0x14: Offset to attributes +- __le16 flags; // 0x16: See RECORD_FLAG +- __le32 used; // 0x18: The size of used part +- __le32 total; // 0x1C: Total record size ++ __le16 seq; // 0x10: Sequence number for this record. ++ __le16 hard_links; // 0x12: The number of hard links to record. ++ __le16 attr_off; // 0x14: Offset to attributes. ++ __le16 flags; // 0x16: See RECORD_FLAG. ++ __le32 used; // 0x18: The size of used part. ++ __le32 total; // 0x1C: Total record size. + +- struct MFT_REF parent_ref; // 0x20: Parent MFT record +- __le16 next_attr_id; // 0x28: The next attribute Id ++ struct MFT_REF parent_ref; // 0x20: Parent MFT record. ++ __le16 next_attr_id; // 0x28: The next attribute Id. + +- __le16 res; // 0x2A: High part of mft record? +- __le32 mft_record; // 0x2C: Current mft record number ++ __le16 res; // 0x2A: High part of MFT record? ++ __le32 mft_record; // 0x2C: Current MFT record number. + __le16 fixups[]; // 0x30: + }; + +@@ -323,16 +314,16 @@ static inline bool clear_rec_inuse(struct MFT_REC *rec) + #define RESIDENT_FLAG_INDEXED 0x01 + + struct ATTR_RESIDENT { +- __le32 data_size; // 0x10: The size of data +- __le16 data_off; // 0x14: Offset to data +- u8 flags; // 0x16: resident flags ( 1 - indexed ) ++ __le32 data_size; // 0x10: The size of data. ++ __le16 data_off; // 0x14: Offset to data. ++ u8 flags; // 0x16: Resident flags ( 1 - indexed ). + u8 res; // 0x17: + }; // sizeof() = 0x18 + + struct ATTR_NONRESIDENT { +- __le64 svcn; // 0x10: Starting VCN of this segment +- __le64 evcn; // 0x18: End VCN of this segment +- __le16 run_off; // 0x20: Offset to packed runs ++ __le64 svcn; // 0x10: Starting VCN of this segment. ++ __le64 evcn; // 0x18: End VCN of this segment. ++ __le16 run_off; // 0x20: Offset to packed runs. + // Unit of Compression size for this stream, expressed + // as a log of the cluster size. + // +@@ -345,13 +336,13 @@ struct ATTR_NONRESIDENT { + // reasonable range of legal values here (1-5?), + // even if the implementation only generates + // a smaller set of values itself. +- u8 c_unit; // 0x22 ++ u8 c_unit; // 0x22: + u8 res1[5]; // 0x23: +- __le64 alloc_size; // 0x28: The allocated size of attribute in bytes ++ __le64 alloc_size; // 0x28: The allocated size of attribute in bytes. + // (multiple of cluster size) +- __le64 data_size; // 0x30: The size of attribute in bytes <= alloc_size +- __le64 valid_size; // 0x38: The size of valid part in bytes <= data_size +- __le64 total_size; // 0x40: The sum of the allocated clusters for a file ++ __le64 data_size; // 0x30: The size of attribute in bytes <= alloc_size. ++ __le64 valid_size; // 0x38: The size of valid part in bytes <= data_size. ++ __le64 total_size; // 0x40: The sum of the allocated clusters for a file. + // (present only for the first segment (0 == vcn) + // of compressed attribute) + +@@ -364,13 +355,13 @@ struct ATTR_NONRESIDENT { + #define ATTR_FLAG_SPARSED cpu_to_le16(0x8000) + + struct ATTRIB { +- enum ATTR_TYPE type; // 0x00: The type of this attribute +- __le32 size; // 0x04: The size of this attribute +- u8 non_res; // 0x08: Is this attribute non-resident ? +- u8 name_len; // 0x09: This attribute name length +- __le16 name_off; // 0x0A: Offset to the attribute name +- __le16 flags; // 0x0C: See ATTR_FLAG_XXX +- __le16 id; // 0x0E: unique id (per record) ++ enum ATTR_TYPE type; // 0x00: The type of this attribute. ++ __le32 size; // 0x04: The size of this attribute. ++ u8 non_res; // 0x08: Is this attribute non-resident? ++ u8 name_len; // 0x09: This attribute name length. ++ __le16 name_off; // 0x0A: Offset to the attribute name. ++ __le16 flags; // 0x0C: See ATTR_FLAG_XXX. ++ __le16 id; // 0x0E: Unique id (per record). + + union { + struct ATTR_RESIDENT res; // 0x10 +@@ -378,7 +369,7 @@ struct ATTRIB { + }; + }; + +-/* Define attribute sizes */ ++/* Define attribute sizes. */ + #define SIZEOF_RESIDENT 0x18 + #define SIZEOF_NONRESIDENT_EX 0x48 + #define SIZEOF_NONRESIDENT 0x40 +@@ -437,7 +428,7 @@ static inline u64 attr_svcn(const struct ATTRIB *attr) + return attr->non_res ? le64_to_cpu(attr->nres.svcn) : 0; + } + +-/* the size of resident attribute by its resident size */ ++/* The size of resident attribute by its resident size. */ + #define BYTES_PER_RESIDENT(b) (0x18 + (b)) + + static_assert(sizeof(struct ATTRIB) == 0x48); +@@ -475,16 +466,16 @@ static inline void *attr_run(const struct ATTRIB *attr) + return Add2Ptr(attr, le16_to_cpu(attr->nres.run_off)); + } + +-/* Standard information attribute (0x10) */ ++/* Standard information attribute (0x10). */ + struct ATTR_STD_INFO { +- __le64 cr_time; // 0x00: File creation file +- __le64 m_time; // 0x08: File modification time +- __le64 c_time; // 0x10: Last time any attribute was modified +- __le64 a_time; // 0x18: File last access time +- enum FILE_ATTRIBUTE fa; // 0x20: Standard DOS attributes & more +- __le32 max_ver_num; // 0x24: Maximum Number of Versions +- __le32 ver_num; // 0x28: Version Number +- __le32 class_id; // 0x2C: Class Id from bidirectional Class Id index ++ __le64 cr_time; // 0x00: File creation file. ++ __le64 m_time; // 0x08: File modification time. ++ __le64 c_time; // 0x10: Last time any attribute was modified. ++ __le64 a_time; // 0x18: File last access time. ++ enum FILE_ATTRIBUTE fa; // 0x20: Standard DOS attributes & more. ++ __le32 max_ver_num; // 0x24: Maximum Number of Versions. ++ __le32 ver_num; // 0x28: Version Number. ++ __le32 class_id; // 0x2C: Class Id from bidirectional Class Id index. + }; + + static_assert(sizeof(struct ATTR_STD_INFO) == 0x30); +@@ -493,17 +484,17 @@ static_assert(sizeof(struct ATTR_STD_INFO) == 0x30); + #define SECURITY_ID_FIRST 0x00000100 + + struct ATTR_STD_INFO5 { +- __le64 cr_time; // 0x00: File creation file +- __le64 m_time; // 0x08: File modification time +- __le64 c_time; // 0x10: Last time any attribute was modified +- __le64 a_time; // 0x18: File last access time +- enum FILE_ATTRIBUTE fa; // 0x20: Standard DOS attributes & more +- __le32 max_ver_num; // 0x24: Maximum Number of Versions +- __le32 ver_num; // 0x28: Version Number +- __le32 class_id; // 0x2C: Class Id from bidirectional Class Id index ++ __le64 cr_time; // 0x00: File creation file. ++ __le64 m_time; // 0x08: File modification time. ++ __le64 c_time; // 0x10: Last time any attribute was modified. ++ __le64 a_time; // 0x18: File last access time. ++ enum FILE_ATTRIBUTE fa; // 0x20: Standard DOS attributes & more. ++ __le32 max_ver_num; // 0x24: Maximum Number of Versions. ++ __le32 ver_num; // 0x28: Version Number. ++ __le32 class_id; // 0x2C: Class Id from bidirectional Class Id index. + + __le32 owner_id; // 0x30: Owner Id of the user owning the file. +- __le32 security_id; // 0x34: The Security Id is a key in the $SII Index and $SDS ++ __le32 security_id; // 0x34: The Security Id is a key in the $SII Index and $SDS. + __le64 quota_charge; // 0x38: + __le64 usn; // 0x40: Last Update Sequence Number of the file. This is a direct + // index into the file $UsnJrnl. If zero, the USN Journal is +@@ -512,16 +503,16 @@ struct ATTR_STD_INFO5 { + + static_assert(sizeof(struct ATTR_STD_INFO5) == 0x48); + +-/* attribute list entry structure (0x20) */ ++/* Attribute list entry structure (0x20) */ + struct ATTR_LIST_ENTRY { +- enum ATTR_TYPE type; // 0x00: The type of attribute +- __le16 size; // 0x04: The size of this record +- u8 name_len; // 0x06: The length of attribute name +- u8 name_off; // 0x07: The offset to attribute name +- __le64 vcn; // 0x08: Starting VCN of this attribute +- struct MFT_REF ref; // 0x10: MFT record number with attribute +- __le16 id; // 0x18: struct ATTRIB ID +- __le16 name[3]; // 0x1A: Just to align. To get real name can use bNameOffset ++ enum ATTR_TYPE type; // 0x00: The type of attribute. ++ __le16 size; // 0x04: The size of this record. ++ u8 name_len; // 0x06: The length of attribute name. ++ u8 name_off; // 0x07: The offset to attribute name. ++ __le64 vcn; // 0x08: Starting VCN of this attribute. ++ struct MFT_REF ref; // 0x10: MFT record number with attribute. ++ __le16 id; // 0x18: struct ATTRIB ID. ++ __le16 name[3]; // 0x1A: Just to align. To get real name can use bNameOffset. + + }; // sizeof(0x20) + +@@ -533,7 +524,7 @@ static inline u32 le_size(u8 name_len) + name_len * sizeof(short), 8); + } + +-/* returns 0 if 'attr' has the same type and name */ ++/* Returns 0 if 'attr' has the same type and name. */ + static inline int le_cmp(const struct ATTR_LIST_ENTRY *le, + const struct ATTRIB *attr) + { +@@ -549,32 +540,32 @@ static inline __le16 const *le_name(const struct ATTR_LIST_ENTRY *le) + return Add2Ptr(le, le->name_off); + } + +-/* File name types (the field type in struct ATTR_FILE_NAME ) */ ++/* File name types (the field type in struct ATTR_FILE_NAME). */ + #define FILE_NAME_POSIX 0 + #define FILE_NAME_UNICODE 1 + #define FILE_NAME_DOS 2 + #define FILE_NAME_UNICODE_AND_DOS (FILE_NAME_DOS | FILE_NAME_UNICODE) + +-/* Filename attribute structure (0x30) */ ++/* Filename attribute structure (0x30). */ + struct NTFS_DUP_INFO { +- __le64 cr_time; // 0x00: File creation file +- __le64 m_time; // 0x08: File modification time +- __le64 c_time; // 0x10: Last time any attribute was modified +- __le64 a_time; // 0x18: File last access time +- __le64 alloc_size; // 0x20: Data attribute allocated size, multiple of cluster size +- __le64 data_size; // 0x28: Data attribute size <= Dataalloc_size +- enum FILE_ATTRIBUTE fa; // 0x30: Standard DOS attributes & more +- __le16 ea_size; // 0x34: Packed EAs +- __le16 reparse; // 0x36: Used by Reparse ++ __le64 cr_time; // 0x00: File creation file. ++ __le64 m_time; // 0x08: File modification time. ++ __le64 c_time; // 0x10: Last time any attribute was modified. ++ __le64 a_time; // 0x18: File last access time. ++ __le64 alloc_size; // 0x20: Data attribute allocated size, multiple of cluster size. ++ __le64 data_size; // 0x28: Data attribute size <= Dataalloc_size. ++ enum FILE_ATTRIBUTE fa; // 0x30: Standard DOS attributes & more. ++ __le16 ea_size; // 0x34: Packed EAs. ++ __le16 reparse; // 0x36: Used by Reparse. + + }; // 0x38 + + struct ATTR_FILE_NAME { +- struct MFT_REF home; // 0x00: MFT record for directory +- struct NTFS_DUP_INFO dup;// 0x08 +- u8 name_len; // 0x40: File name length in words +- u8 type; // 0x41: File name type +- __le16 name[]; // 0x42: File name ++ struct MFT_REF home; // 0x00: MFT record for directory. ++ struct NTFS_DUP_INFO dup;// 0x08: ++ u8 name_len; // 0x40: File name length in words. ++ u8 type; // 0x41: File name type. ++ __le16 name[]; // 0x42: File name. + }; + + static_assert(sizeof(((struct ATTR_FILE_NAME *)NULL)->dup) == 0x38); +@@ -589,7 +580,7 @@ static inline struct ATTRIB *attr_from_name(struct ATTR_FILE_NAME *fname) + + static inline u16 fname_full_size(const struct ATTR_FILE_NAME *fname) + { +- // don't return struct_size(fname, name, fname->name_len); ++ /* Don't return struct_size(fname, name, fname->name_len); */ + return offsetof(struct ATTR_FILE_NAME, name) + + fname->name_len * sizeof(short); + } +@@ -603,32 +594,32 @@ static inline u8 paired_name(u8 type) + return FILE_NAME_POSIX; + } + +-/* Index entry defines ( the field flags in NtfsDirEntry ) */ ++/* Index entry defines ( the field flags in NtfsDirEntry ). */ + #define NTFS_IE_HAS_SUBNODES cpu_to_le16(1) + #define NTFS_IE_LAST cpu_to_le16(2) + +-/* Directory entry structure */ ++/* Directory entry structure. */ + struct NTFS_DE { + union { +- struct MFT_REF ref; // 0x00: MFT record number with this file ++ struct MFT_REF ref; // 0x00: MFT record number with this file. + struct { + __le16 data_off; // 0x00: + __le16 data_size; // 0x02: +- __le32 res; // 0x04: must be 0 ++ __le32 res; // 0x04: Must be 0. + } view; + }; +- __le16 size; // 0x08: The size of this entry +- __le16 key_size; // 0x0A: The size of File name length in bytes + 0x42 +- __le16 flags; // 0x0C: Entry flags: NTFS_IE_XXX ++ __le16 size; // 0x08: The size of this entry. ++ __le16 key_size; // 0x0A: The size of File name length in bytes + 0x42. ++ __le16 flags; // 0x0C: Entry flags: NTFS_IE_XXX. + __le16 res; // 0x0E: + +- // Here any indexed attribute can be placed ++ // Here any indexed attribute can be placed. + // One of them is: + // struct ATTR_FILE_NAME AttrFileName; + // + + // The last 8 bytes of this structure contains +- // the VBN of subnode ++ // the VBN of subnode. + // !!! Note !!! + // This field is presented only if (flags & NTFS_IE_HAS_SUBNODES) + // __le64 vbn; +@@ -698,11 +689,11 @@ static inline bool de_has_vcn_ex(const struct NTFS_DE *e) + + struct INDEX_HDR { + __le32 de_off; // 0x00: The offset from the start of this structure +- // to the first NTFS_DE ++ // to the first NTFS_DE. + __le32 used; // 0x04: The size of this structure plus all +- // entries (quad-word aligned) +- __le32 total; // 0x08: The allocated size of for this structure plus all entries +- u8 flags; // 0x0C: 0x00 = Small directory, 0x01 = Large directory ++ // entries (quad-word aligned). ++ __le32 total; // 0x08: The allocated size of for this structure plus all entries. ++ u8 flags; // 0x0C: 0x00 = Small directory, 0x01 = Large directory. + u8 res[3]; + + // +@@ -773,7 +764,7 @@ static inline bool ib_is_leaf(const struct INDEX_BUFFER *ib) + return !(ib->ihdr.flags & 1); + } + +-/* Index root structure ( 0x90 ) */ ++/* Index root structure ( 0x90 ). */ + enum COLLATION_RULE { + NTFS_COLLATION_TYPE_BINARY = cpu_to_le32(0), + // $I30 +@@ -792,10 +783,10 @@ static_assert(sizeof(enum COLLATION_RULE) == 4); + + // + struct INDEX_ROOT { +- enum ATTR_TYPE type; // 0x00: The type of attribute to index on +- enum COLLATION_RULE rule; // 0x04: The rule +- __le32 index_block_size;// 0x08: The size of index record +- u8 index_block_clst; // 0x0C: The number of clusters or sectors per index ++ enum ATTR_TYPE type; // 0x00: The type of attribute to index on. ++ enum COLLATION_RULE rule; // 0x04: The rule. ++ __le32 index_block_size;// 0x08: The size of index record. ++ u8 index_block_clst; // 0x0C: The number of clusters or sectors per index. + u8 res[3]; + struct INDEX_HDR ihdr; // 0x10: + }; +@@ -824,24 +815,24 @@ struct VOLUME_INFO { + #define NTFS_ATTR_MUST_BE_RESIDENT cpu_to_le32(0x00000040) + #define NTFS_ATTR_LOG_ALWAYS cpu_to_le32(0x00000080) + +-/* $AttrDef file entry */ ++/* $AttrDef file entry. */ + struct ATTR_DEF_ENTRY { +- __le16 name[0x40]; // 0x00: Attr name +- enum ATTR_TYPE type; // 0x80: struct ATTRIB type ++ __le16 name[0x40]; // 0x00: Attr name. ++ enum ATTR_TYPE type; // 0x80: struct ATTRIB type. + __le32 res; // 0x84: + enum COLLATION_RULE rule; // 0x88: +- __le32 flags; // 0x8C: NTFS_ATTR_XXX (see above) +- __le64 min_sz; // 0x90: Minimum attribute data size +- __le64 max_sz; // 0x98: Maximum attribute data size ++ __le32 flags; // 0x8C: NTFS_ATTR_XXX (see above). ++ __le64 min_sz; // 0x90: Minimum attribute data size. ++ __le64 max_sz; // 0x98: Maximum attribute data size. + }; + + static_assert(sizeof(struct ATTR_DEF_ENTRY) == 0xa0); + + /* Object ID (0x40) */ + struct OBJECT_ID { +- struct GUID ObjId; // 0x00: Unique Id assigned to file +- struct GUID BirthVolumeId;// 0x10: Birth Volume Id is the Object Id of the Volume on +- // which the Object Id was allocated. It never changes ++ struct GUID ObjId; // 0x00: Unique Id assigned to file. ++ struct GUID BirthVolumeId; // 0x10: Birth Volume Id is the Object Id of the Volume on. ++ // which the Object Id was allocated. It never changes. + struct GUID BirthObjectId; // 0x20: Birth Object Id is the first Object Id that was + // ever assigned to this MFT Record. I.e. If the Object Id + // is changed for some reason, this field will reflect the +@@ -857,15 +848,15 @@ static_assert(sizeof(struct OBJECT_ID) == 0x40); + /* O Directory entry structure ( rule = 0x13 ) */ + struct NTFS_DE_O { + struct NTFS_DE de; +- struct GUID ObjId; // 0x10: Unique Id assigned to file +- struct MFT_REF ref; // 0x20: MFT record number with this file ++ struct GUID ObjId; // 0x10: Unique Id assigned to file. ++ struct MFT_REF ref; // 0x20: MFT record number with this file. + struct GUID BirthVolumeId; // 0x28: Birth Volume Id is the Object Id of the Volume on +- // which the Object Id was allocated. It never changes ++ // which the Object Id was allocated. It never changes. + struct GUID BirthObjectId; // 0x38: Birth Object Id is the first Object Id that was + // ever assigned to this MFT Record. I.e. If the Object Id + // is changed for some reason, this field will reflect the + // original value of the Object Id. +- // This field is valid if data_size == 0x48 ++ // This field is valid if data_size == 0x48. + struct GUID BirthDomainId; // 0x48: Domain Id is currently unused but it is intended + // to be used in a network environment where the local + // machine is part of a Windows 2000 Domain. This may be +@@ -907,13 +898,13 @@ struct SECURITY_KEY { + + /* Security descriptors (the content of $Secure::SDS data stream) */ + struct SECURITY_HDR { +- struct SECURITY_KEY key; // 0x00: Security Key +- __le64 off; // 0x08: Offset of this entry in the file +- __le32 size; // 0x10: Size of this entry, 8 byte aligned +- // +- // Security descriptor itself is placed here +- // Total size is 16 byte aligned +- // ++ struct SECURITY_KEY key; // 0x00: Security Key. ++ __le64 off; // 0x08: Offset of this entry in the file. ++ __le32 size; // 0x10: Size of this entry, 8 byte aligned. ++ /* ++ * Security descriptor itself is placed here. ++ * Total size is 16 byte aligned. ++ */ + } __packed; + + #define SIZEOF_SECURITY_HDR 0x14 +@@ -948,8 +939,8 @@ static_assert(offsetof(struct REPARSE_KEY, ref) == 0x04); + /* Reparse Directory entry structure */ + struct NTFS_DE_R { + struct NTFS_DE de; +- struct REPARSE_KEY key; // 0x10: Reparse Key +- u32 zero; // 0x1c ++ struct REPARSE_KEY key; // 0x10: Reparse Key. ++ u32 zero; // 0x1c: + }; // sizeof() = 0x20 + + static_assert(sizeof(struct NTFS_DE_R) == 0x20); +@@ -991,69 +982,63 @@ struct REPARSE_POINT { + + static_assert(sizeof(struct REPARSE_POINT) == 0x18); + +-// +-// Maximum allowed size of the reparse data. +-// ++/* Maximum allowed size of the reparse data. */ + #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) + +-// +-// The value of the following constant needs to satisfy the following +-// conditions: +-// (1) Be at least as large as the largest of the reserved tags. +-// (2) Be strictly smaller than all the tags in use. +-// ++/* ++ * The value of the following constant needs to satisfy the following ++ * conditions: ++ * (1) Be at least as large as the largest of the reserved tags. ++ * (2) Be strictly smaller than all the tags in use. ++ */ + #define IO_REPARSE_TAG_RESERVED_RANGE 1 + +-// +-// The reparse tags are a ULONG. The 32 bits are laid out as follows: +-// +-// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +-// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +-// +-+-+-+-+-----------------------+-------------------------------+ +-// |M|R|N|R| Reserved bits | Reparse Tag Value | +-// +-+-+-+-+-----------------------+-------------------------------+ +-// +-// M is the Microsoft bit. When set to 1, it denotes a tag owned by Microsoft. +-// All ISVs must use a tag with a 0 in this position. +-// Note: If a Microsoft tag is used by non-Microsoft software, the +-// behavior is not defined. +-// +-// R is reserved. Must be zero for non-Microsoft tags. +-// +-// N is name surrogate. When set to 1, the file represents another named +-// entity in the system. +-// +-// The M and N bits are OR-able. +-// The following macros check for the M and N bit values: +-// ++/* ++ * The reparse tags are a ULONG. The 32 bits are laid out as follows: ++ * ++ * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 ++ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ++ * +-+-+-+-+-----------------------+-------------------------------+ ++ * |M|R|N|R| Reserved bits | Reparse Tag Value | ++ * +-+-+-+-+-----------------------+-------------------------------+ ++ * ++ * M is the Microsoft bit. When set to 1, it denotes a tag owned by Microsoft. ++ * All ISVs must use a tag with a 0 in this position. ++ * Note: If a Microsoft tag is used by non-Microsoft software, the ++ * behavior is not defined. ++ * ++ * R is reserved. Must be zero for non-Microsoft tags. ++ * ++ * N is name surrogate. When set to 1, the file represents another named ++ * entity in the system. ++ * ++ * The M and N bits are OR-able. ++ * The following macros check for the M and N bit values: ++ */ + +-// +-// Macro to determine whether a reparse point tag corresponds to a tag +-// owned by Microsoft. +-// ++/* ++ * Macro to determine whether a reparse point tag corresponds to a tag ++ * owned by Microsoft. ++ */ + #define IsReparseTagMicrosoft(_tag) (((_tag)&IO_REPARSE_TAG_MICROSOFT)) + +-// +-// Macro to determine whether a reparse point tag is a name surrogate +-// ++/* Macro to determine whether a reparse point tag is a name surrogate. */ + #define IsReparseTagNameSurrogate(_tag) (((_tag)&IO_REPARSE_TAG_NAME_SURROGATE)) + +-// +-// The following constant represents the bits that are valid to use in +-// reparse tags. +-// ++/* ++ * The following constant represents the bits that are valid to use in ++ * reparse tags. ++ */ + #define IO_REPARSE_TAG_VALID_VALUES 0xF000FFFF + +-// +-// Macro to determine whether a reparse tag is a valid tag. +-// ++/* ++ * Macro to determine whether a reparse tag is a valid tag. ++ */ + #define IsReparseTagValid(_tag) \ + (!((_tag) & ~IO_REPARSE_TAG_VALID_VALUES) && \ + ((_tag) > IO_REPARSE_TAG_RESERVED_RANGE)) + +-// +-// Microsoft tags for reparse points. +-// ++/* Microsoft tags for reparse points. */ + + enum IO_REPARSE_TAG { + IO_REPARSE_TAG_SYMBOLIC_LINK = cpu_to_le32(0), +@@ -1066,62 +1051,48 @@ enum IO_REPARSE_TAG { + IO_REPARSE_TAG_DEDUP = cpu_to_le32(0x80000013), + IO_REPARSE_TAG_COMPRESS = cpu_to_le32(0x80000017), + +- // +- // The reparse tag 0x80000008 is reserved for Microsoft internal use +- // (may be published in the future) +- // ++ /* ++ * The reparse tag 0x80000008 is reserved for Microsoft internal use. ++ * May be published in the future. ++ */ + +- // +- // Microsoft reparse tag reserved for DFS +- // +- IO_REPARSE_TAG_DFS = cpu_to_le32(0x8000000A), ++ /* Microsoft reparse tag reserved for DFS */ ++ IO_REPARSE_TAG_DFS = cpu_to_le32(0x8000000A), + +- // +- // Microsoft reparse tag reserved for the file system filter manager +- // ++ /* Microsoft reparse tag reserved for the file system filter manager. */ + IO_REPARSE_TAG_FILTER_MANAGER = cpu_to_le32(0x8000000B), + +- // +- // Non-Microsoft tags for reparse points +- // ++ /* Non-Microsoft tags for reparse points */ + +- // +- // Tag allocated to CONGRUENT, May 2000. Used by IFSTEST +- // ++ /* Tag allocated to CONGRUENT, May 2000. Used by IFSTEST. */ + IO_REPARSE_TAG_IFSTEST_CONGRUENT = cpu_to_le32(0x00000009), + +- // +- // Tag allocated to ARKIVIO +- // +- IO_REPARSE_TAG_ARKIVIO = cpu_to_le32(0x0000000C), ++ /* Tag allocated to ARKIVIO. */ ++ IO_REPARSE_TAG_ARKIVIO = cpu_to_le32(0x0000000C), + +- // +- // Tag allocated to SOLUTIONSOFT +- // ++ /* Tag allocated to SOLUTIONSOFT. */ + IO_REPARSE_TAG_SOLUTIONSOFT = cpu_to_le32(0x2000000D), + +- // +- // Tag allocated to COMMVAULT +- // ++ /* Tag allocated to COMMVAULT. */ + IO_REPARSE_TAG_COMMVAULT = cpu_to_le32(0x0000000E), + +- // OneDrive?? +- IO_REPARSE_TAG_CLOUD = cpu_to_le32(0x9000001A), +- IO_REPARSE_TAG_CLOUD_1 = cpu_to_le32(0x9000101A), +- IO_REPARSE_TAG_CLOUD_2 = cpu_to_le32(0x9000201A), +- IO_REPARSE_TAG_CLOUD_3 = cpu_to_le32(0x9000301A), +- IO_REPARSE_TAG_CLOUD_4 = cpu_to_le32(0x9000401A), +- IO_REPARSE_TAG_CLOUD_5 = cpu_to_le32(0x9000501A), +- IO_REPARSE_TAG_CLOUD_6 = cpu_to_le32(0x9000601A), +- IO_REPARSE_TAG_CLOUD_7 = cpu_to_le32(0x9000701A), +- IO_REPARSE_TAG_CLOUD_8 = cpu_to_le32(0x9000801A), +- IO_REPARSE_TAG_CLOUD_9 = cpu_to_le32(0x9000901A), +- IO_REPARSE_TAG_CLOUD_A = cpu_to_le32(0x9000A01A), +- IO_REPARSE_TAG_CLOUD_B = cpu_to_le32(0x9000B01A), +- IO_REPARSE_TAG_CLOUD_C = cpu_to_le32(0x9000C01A), +- IO_REPARSE_TAG_CLOUD_D = cpu_to_le32(0x9000D01A), +- IO_REPARSE_TAG_CLOUD_E = cpu_to_le32(0x9000E01A), +- IO_REPARSE_TAG_CLOUD_F = cpu_to_le32(0x9000F01A), ++ /* OneDrive?? */ ++ IO_REPARSE_TAG_CLOUD = cpu_to_le32(0x9000001A), ++ IO_REPARSE_TAG_CLOUD_1 = cpu_to_le32(0x9000101A), ++ IO_REPARSE_TAG_CLOUD_2 = cpu_to_le32(0x9000201A), ++ IO_REPARSE_TAG_CLOUD_3 = cpu_to_le32(0x9000301A), ++ IO_REPARSE_TAG_CLOUD_4 = cpu_to_le32(0x9000401A), ++ IO_REPARSE_TAG_CLOUD_5 = cpu_to_le32(0x9000501A), ++ IO_REPARSE_TAG_CLOUD_6 = cpu_to_le32(0x9000601A), ++ IO_REPARSE_TAG_CLOUD_7 = cpu_to_le32(0x9000701A), ++ IO_REPARSE_TAG_CLOUD_8 = cpu_to_le32(0x9000801A), ++ IO_REPARSE_TAG_CLOUD_9 = cpu_to_le32(0x9000901A), ++ IO_REPARSE_TAG_CLOUD_A = cpu_to_le32(0x9000A01A), ++ IO_REPARSE_TAG_CLOUD_B = cpu_to_le32(0x9000B01A), ++ IO_REPARSE_TAG_CLOUD_C = cpu_to_le32(0x9000C01A), ++ IO_REPARSE_TAG_CLOUD_D = cpu_to_le32(0x9000D01A), ++ IO_REPARSE_TAG_CLOUD_E = cpu_to_le32(0x9000E01A), ++ IO_REPARSE_TAG_CLOUD_F = cpu_to_le32(0x9000F01A), + + }; + +@@ -1134,7 +1105,7 @@ struct REPARSE_DATA_BUFFER { + __le16 Reserved; + + union { +- // If ReparseTag == 0xA0000003 (IO_REPARSE_TAG_MOUNT_POINT) ++ /* If ReparseTag == 0xA0000003 (IO_REPARSE_TAG_MOUNT_POINT) */ + struct { + __le16 SubstituteNameOffset; // 0x08 + __le16 SubstituteNameLength; // 0x0A +@@ -1143,8 +1114,10 @@ struct REPARSE_DATA_BUFFER { + __le16 PathBuffer[]; // 0x10 + } MountPointReparseBuffer; + +- // If ReparseTag == 0xA000000C (IO_REPARSE_TAG_SYMLINK) +- // https://msdn.microsoft.com/en-us/library/cc232006.aspx ++ /* ++ * If ReparseTag == 0xA000000C (IO_REPARSE_TAG_SYMLINK) ++ * https://msdn.microsoft.com/en-us/library/cc232006.aspx ++ */ + struct { + __le16 SubstituteNameOffset; // 0x08 + __le16 SubstituteNameLength; // 0x0A +@@ -1155,19 +1128,20 @@ struct REPARSE_DATA_BUFFER { + __le16 PathBuffer[]; // 0x14 + } SymbolicLinkReparseBuffer; + +- // If ReparseTag == 0x80000017U ++ /* If ReparseTag == 0x80000017U */ + struct { + __le32 WofVersion; // 0x08 == 1 +- /* 1 - WIM backing provider ("WIMBoot"), ++ /* ++ * 1 - WIM backing provider ("WIMBoot"), + * 2 - System compressed file provider + */ +- __le32 WofProvider; // 0x0C ++ __le32 WofProvider; // 0x0C: + __le32 ProviderVer; // 0x10: == 1 WOF_FILE_PROVIDER_CURRENT_VERSION == 1 + __le32 CompressionFormat; // 0x14: 0, 1, 2, 3. See WOF_COMPRESSION_XXX + } CompressReparseBuffer; + + struct { +- u8 DataBuffer[1]; // 0x08 ++ u8 DataBuffer[1]; // 0x08: + } GenericReparseBuffer; + }; + }; +@@ -1175,13 +1149,14 @@ struct REPARSE_DATA_BUFFER { + /* ATTR_EA_INFO (0xD0) */ + + #define FILE_NEED_EA 0x80 // See ntifs.h +-/* FILE_NEED_EA, indicates that the file to which the EA belongs cannot be ++/* ++ *FILE_NEED_EA, indicates that the file to which the EA belongs cannot be + * interpreted without understanding the associated extended attributes. + */ + struct EA_INFO { +- __le16 size_pack; // 0x00: Size of buffer to hold in packed form +- __le16 count; // 0x02: Count of EA's with FILE_NEED_EA bit set +- __le32 size; // 0x04: Size of buffer to hold in unpacked form ++ __le16 size_pack; // 0x00: Size of buffer to hold in packed form. ++ __le16 count; // 0x02: Count of EA's with FILE_NEED_EA bit set. ++ __le32 size; // 0x04: Size of buffer to hold in unpacked form. + }; + + static_assert(sizeof(struct EA_INFO) == 8); +@@ -1189,10 +1164,10 @@ static_assert(sizeof(struct EA_INFO) == 8); + /* ATTR_EA (0xE0) */ + struct EA_FULL { + __le32 size; // 0x00: (not in packed) +- u8 flags; // 0x04 +- u8 name_len; // 0x05 +- __le16 elength; // 0x06 +- u8 name[]; // 0x08 ++ u8 flags; // 0x04: ++ u8 name_len; // 0x05: ++ __le16 elength; // 0x06: ++ u8 name[]; // 0x08: + }; + + static_assert(offsetof(struct EA_FULL, name) == 8); +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index e3a667e9838f3290032e90aaded68c5b09edbb70..64ef92e16363c4973be6438efdeea0663ce10e84 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -17,33 +17,33 @@ + #define MAXIMUM_BYTES_PER_INDEX 4096 + #define NTFS_BLOCKS_PER_INODE (MAXIMUM_BYTES_PER_INDEX / 512) + +-/* ntfs specific error code when fixup failed*/ ++/* NTFS specific error code when fixup failed. */ + #define E_NTFS_FIXUP 555 +-/* ntfs specific error code about resident->nonresident*/ ++/* NTFS specific error code about resident->nonresident. */ + #define E_NTFS_NONRESIDENT 556 +-/* ntfs specific error code about punch hole*/ ++/* NTFS specific error code about punch hole. */ + #define E_NTFS_NOTALIGNED 557 + + + /* sbi->flags */ + #define NTFS_FLAGS_NODISCARD 0x00000001 +-/* Set when LogFile is replaying */ ++/* Set when LogFile is replaying. */ + #define NTFS_FLAGS_LOG_REPLAYING 0x00000008 +-/* Set when we changed first MFT's which copy must be updated in $MftMirr */ ++/* Set when we changed first MFT's which copy must be updated in $MftMirr. */ + #define NTFS_FLAGS_MFTMIRR 0x00001000 + #define NTFS_FLAGS_NEED_REPLAY 0x04000000 + + + /* ni->ni_flags */ + /* +- * Data attribute is external compressed (lzx/xpress) ++ * Data attribute is external compressed (LZX/Xpress) + * 1 - WOF_COMPRESSION_XPRESS4K + * 2 - WOF_COMPRESSION_XPRESS8K + * 3 - WOF_COMPRESSION_XPRESS16K + * 4 - WOF_COMPRESSION_LZX32K + */ + #define NI_FLAG_COMPRESSED_MASK 0x0000000f +-/* Data attribute is deduplicated */ ++/* Data attribute is deduplicated. */ + #define NI_FLAG_DEDUPLICATED 0x00000010 + #define NI_FLAG_EA 0x00000020 + #define NI_FLAG_DIR 0x00000040 +@@ -59,29 +59,29 @@ struct ntfs_mount_options { + u16 fs_fmask_inv; + u16 fs_dmask_inv; + +- unsigned uid : 1, /* uid was set */ +- gid : 1, /* gid was set */ +- fmask : 1, /* fmask was set */ +- dmask : 1, /*dmask was set*/ +- sys_immutable : 1, /* immutable system files */ +- discard : 1, /* issue discard requests on deletions */ +- sparse : 1, /*create sparse files*/ +- showmeta : 1, /*show meta files*/ +- nohidden : 1, /*do not show hidden files*/ +- force : 1, /*rw mount dirty volume*/ +- no_acs_rules : 1, /*exclude acs rules*/ +- prealloc : 1 /*preallocate space when file is growing*/ ++ unsigned uid : 1, /* uid was set. */ ++ gid : 1, /* gid was set. */ ++ fmask : 1, /* fmask was set. */ ++ dmask : 1, /* dmask was set. */ ++ sys_immutable : 1,/* Immutable system files. */ ++ discard : 1, /* Issue discard requests on deletions. */ ++ sparse : 1, /* Create sparse files. */ ++ showmeta : 1, /* Show meta files. */ ++ nohidden : 1, /* Do not show hidden files. */ ++ force : 1, /* Rw mount dirty volume. */ ++ no_acs_rules : 1,/*Exclude acs rules. */ ++ prealloc : 1 /* Preallocate space when file is growing. */ + ; + }; + +-/* special value to unpack and deallocate*/ ++/* Special value to unpack and deallocate. */ + #define RUN_DEALLOCATE ((struct runs_tree *)(size_t)1) + +-/* TODO: use rb tree instead of array */ ++/* TODO: Use rb tree instead of array. */ + struct runs_tree { + struct ntfs_run *runs; +- size_t count; // Currently used size a ntfs_run storage. +- size_t allocated; // Currently allocated ntfs_run storage size. ++ size_t count; /* Currently used size a ntfs_run storage. */ ++ size_t allocated; /* Currently allocated ntfs_run storage size. */ + }; + + struct ntfs_buffers { +@@ -94,8 +94,8 @@ struct ntfs_buffers { + }; + + enum ALLOCATE_OPT { +- ALLOCATE_DEF = 0, // Allocate all clusters +- ALLOCATE_MFT = 1, // Allocate for MFT ++ ALLOCATE_DEF = 0, // Allocate all clusters. ++ ALLOCATE_MFT = 1, // Allocate for MFT. + }; + + enum bitmap_mutex_classes { +@@ -110,29 +110,29 @@ struct wnd_bitmap { + struct runs_tree run; + size_t nbits; + +- size_t total_zeroes; // total number of free bits +- u16 *free_bits; // free bits in each window ++ size_t total_zeroes; // Total number of free bits. ++ u16 *free_bits; // Free bits in each window. + size_t nwnd; +- u32 bits_last; // bits in last window ++ u32 bits_last; // Bits in last window. + +- struct rb_root start_tree; // extents, sorted by 'start' +- struct rb_root count_tree; // extents, sorted by 'count + start' +- size_t count; // extents count ++ struct rb_root start_tree; // Extents, sorted by 'start'. ++ struct rb_root count_tree; // Extents, sorted by 'count + start'. ++ size_t count; // Extents count. + + /* +- * -1 Tree is activated but not updated (too many fragments) +- * 0 - Tree is not activated +- * 1 - Tree is activated and updated ++ * -1 Tree is activated but not updated (too many fragments). ++ * 0 - Tree is not activated. ++ * 1 - Tree is activated and updated. + */ + int uptodated; +- size_t extent_min; // Minimal extent used while building +- size_t extent_max; // Upper estimate of biggest free block ++ size_t extent_min; // Minimal extent used while building. ++ size_t extent_max; // Upper estimate of biggest free block. + + /* Zone [bit, end) */ + size_t zone_bit; + size_t zone_end; + +- bool set_tail; // not necessary in driver ++ bool set_tail; // Not necessary in driver. + bool inited; + }; + +@@ -149,14 +149,14 @@ enum index_mutex_classed { + INDEX_MUTEX_TOTAL + }; + +-/* ntfs_index - allocation unit inside directory */ ++/* ntfs_index - Allocation unit inside directory. */ + struct ntfs_index { + struct runs_tree bitmap_run; + struct runs_tree alloc_run; + /* read/write access to 'bitmap_run'/'alloc_run' while ntfs_readdir */ + struct rw_semaphore run_lock; + +- /*TODO: remove 'cmp'*/ ++ /*TODO: Remove 'cmp'. */ + NTFS_CMP_FUNC cmp; + + u8 index_bits; // log2(root->index_block_size) +@@ -165,10 +165,10 @@ struct ntfs_index { + u8 type; // index_mutex_classed + }; + +-/* Minimum mft zone */ ++/* Minimum MFT zone. */ + #define NTFS_MIN_MFT_ZONE 100 + +-/* ntfs file system in-core superblock data */ ++/* Ntfs file system in-core superblock data. */ + struct ntfs_sb_info { + struct super_block *sb; + +@@ -189,23 +189,23 @@ struct ntfs_sb_info { + u8 cluster_bits; + u8 record_bits; + +- u64 maxbytes; // Maximum size for normal files +- u64 maxbytes_sparse; // Maximum size for sparse file ++ u64 maxbytes; // Maximum size for normal files. ++ u64 maxbytes_sparse; // Maximum size for sparse file. + +- u32 flags; // See NTFS_FLAGS_XXX ++ u32 flags; // See NTFS_FLAGS_XXX. + +- CLST bad_clusters; // The count of marked bad clusters ++ CLST bad_clusters; // The count of marked bad clusters. + +- u16 max_bytes_per_attr; // maximum attribute size in record +- u16 attr_size_tr; // attribute size threshold (320 bytes) ++ u16 max_bytes_per_attr; // Maximum attribute size in record. ++ u16 attr_size_tr; // Attribute size threshold (320 bytes). + +- /* Records in $Extend */ ++ /* Records in $Extend. */ + CLST objid_no; + CLST quota_no; + CLST reparse_no; + CLST usn_jrnl_no; + +- struct ATTR_DEF_ENTRY *def_table; // attribute definition table ++ struct ATTR_DEF_ENTRY *def_table; // Attribute definition table. + u32 def_entries; + u32 ea_max_size; + +@@ -218,13 +218,13 @@ struct ntfs_sb_info { + struct ntfs_inode *ni; + struct wnd_bitmap bitmap; // $MFT::Bitmap + /* +- * MFT records [11-24) used to expand MFT itself ++ * MFT records [11-24) used to expand MFT itself. + * They always marked as used in $MFT::Bitmap +- * 'reserved_bitmap' contains real bitmap of these records ++ * 'reserved_bitmap' contains real bitmap of these records. + */ +- ulong reserved_bitmap; // bitmap of used records [11 - 24) ++ ulong reserved_bitmap; // Bitmap of used records [11 - 24) + size_t next_free; // The next record to allocate from +- size_t used; // mft valid size in records ++ size_t used; // MFT valid size in records. + u32 recs_mirr; // Number of records in MFTMirr + u8 next_reserved; + u8 reserved_bitmap_inited; +@@ -236,15 +236,15 @@ struct ntfs_sb_info { + } used; + + struct { +- u64 size; // in bytes +- u64 blocks; // in blocks ++ u64 size; // In bytes. ++ u64 blocks; // In blocks. + u64 ser_num; + struct ntfs_inode *ni; +- __le16 flags; // cached current VOLUME_INFO::flags, VOLUME_FLAG_DIRTY ++ __le16 flags; // Cached current VOLUME_INFO::flags, VOLUME_FLAG_DIRTY. + u8 major_ver; + u8 minor_ver; + char label[65]; +- bool real_dirty; /* real fs state*/ ++ bool real_dirty; // Real fs state. + } volume; + + struct { +@@ -283,9 +283,7 @@ struct ntfs_sb_info { + struct ratelimit_state msg_ratelimit; + }; + +-/* +- * one MFT record(usually 1024 bytes), consists of attributes +- */ ++/* One MFT record(usually 1024 bytes), consists of attributes. */ + struct mft_inode { + struct rb_node node; + struct ntfs_sb_info *sbi; +@@ -297,7 +295,7 @@ struct mft_inode { + bool dirty; + }; + +-/* nested class for ntfs_inode::ni_lock */ ++/* Nested class for ntfs_inode::ni_lock. */ + enum ntfs_inode_mutex_lock_class { + NTFS_INODE_MUTEX_DIRTY, + NTFS_INODE_MUTEX_SECURITY, +@@ -308,29 +306,31 @@ enum ntfs_inode_mutex_lock_class { + }; + + /* +- * ntfs inode - extends linux inode. consists of one or more mft inodes ++ * sturct ntfs_inode ++ * ++ * Ntfs inode - extends linux inode. consists of one or more MFT inodes. + */ + struct ntfs_inode { + struct mft_inode mi; // base record + + /* +- * Valid size: [0 - i_valid) - these range in file contains valid data +- * Range [i_valid - inode->i_size) - contains 0 +- * Usually i_valid <= inode->i_size ++ * Valid size: [0 - i_valid) - these range in file contains valid data. ++ * Range [i_valid - inode->i_size) - contains 0. ++ * Usually i_valid <= inode->i_size. + */ + u64 i_valid; + struct timespec64 i_crtime; + + struct mutex ni_lock; + +- /* file attributes from std */ ++ /* File attributes from std. */ + enum FILE_ATTRIBUTE std_fa; + __le32 std_security_id; + + /* +- * tree of mft_inode +- * not empty when primary MFT record (usually 1024 bytes) can't save all attributes +- * e.g. file becomes too fragmented or contains a lot of names ++ * Tree of mft_inode. ++ * Not empty when primary MFT record (usually 1024 bytes) can't save all attributes ++ * e.g. file becomes too fragmented or contains a lot of names. + */ + struct rb_root mi_tree; + +@@ -352,7 +352,7 @@ struct ntfs_inode { + + struct { + struct runs_tree run; +- struct ATTR_LIST_ENTRY *le; // 1K aligned memory ++ struct ATTR_LIST_ENTRY *le; // 1K aligned memory. + size_t size; + bool dirty; + } attr_list; +@@ -381,7 +381,7 @@ enum REPARSE_SIGN { + REPARSE_LINK = 3 + }; + +-/* functions from attrib.c*/ ++/* Functions from attrib.c */ + int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni, + struct runs_tree *run, const CLST *vcn); + int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, +@@ -416,7 +416,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes); + int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size); + +-/* functions from attrlist.c*/ ++/* Functions from attrlist.c */ + void al_destroy(struct ntfs_inode *ni); + bool al_verify(struct ntfs_inode *ni); + int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr); +@@ -442,12 +442,12 @@ static inline size_t al_aligned(size_t size) + return (size + 1023) & ~(size_t)1023; + } + +-/* globals from bitfunc.c */ ++/* Globals from bitfunc.c */ + bool are_bits_clear(const ulong *map, size_t bit, size_t nbits); + bool are_bits_set(const ulong *map, size_t bit, size_t nbits); + size_t get_set_bits_ex(const ulong *map, size_t bit, size_t nbits); + +-/* globals from dir.c */ ++/* Globals from dir.c */ + int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + u8 *buf, int buf_len); + int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, +@@ -458,7 +458,7 @@ struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni, + bool dir_is_empty(struct inode *dir); + extern const struct file_operations ntfs_dir_operations; + +-/* globals from file.c*/ ++/* Globals from file.c */ + int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, + struct kstat *stat, u32 request_mask, u32 flags); + void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn, +@@ -472,7 +472,7 @@ extern const struct inode_operations ntfs_special_inode_operations; + extern const struct inode_operations ntfs_file_inode_operations; + extern const struct file_operations ntfs_file_operations; + +-/* globals from frecord.c */ ++/* Globals from frecord.c */ + void ni_remove_mi(struct ntfs_inode *ni, struct mft_inode *mi); + struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni); + struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni); +@@ -529,10 +529,10 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + u32 pages_per_frame); + +-/* globals from fslog.c */ ++/* Globals from fslog.c */ + int log_replay(struct ntfs_inode *ni, bool *initialized); + +-/* globals from fsntfs.c */ ++/* Globals from fsntfs.c */ + bool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes); + int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, + bool simple); +@@ -598,7 +598,7 @@ int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag, + void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim); + int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim); + +-/* globals from index.c */ ++/* Globals from index.c */ + int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit); + void fnd_clear(struct ntfs_fnd *fnd); + static inline struct ntfs_fnd *fnd_get(void) +@@ -638,7 +638,7 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, + const struct ATTR_FILE_NAME *fname, + const struct NTFS_DUP_INFO *dup, int sync); + +-/* globals from inode.c */ ++/* Globals from inode.c */ + struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref, + const struct cpu_str *name); + int ntfs_set_size(struct inode *inode, u64 new_size); +@@ -662,7 +662,7 @@ extern const struct inode_operations ntfs_link_inode_operations; + extern const struct address_space_operations ntfs_aops; + extern const struct address_space_operations ntfs_aops_cmpr; + +-/* globals from name_i.c*/ ++/* Globals from name_i.c */ + int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name, + const struct cpu_str *uni); + struct dentry *ntfs3_get_parent(struct dentry *child); +@@ -670,7 +670,7 @@ struct dentry *ntfs3_get_parent(struct dentry *child); + extern const struct inode_operations ntfs_dir_inode_operations; + extern const struct inode_operations ntfs_special_inode_operations; + +-/* globals from record.c */ ++/* Globals from record.c */ + int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi); + void mi_put(struct mft_inode *mi); + int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno); +@@ -724,7 +724,7 @@ static inline void mi_get_ref(const struct mft_inode *mi, struct MFT_REF *ref) + ref->seq = mi->mrec->seq; + } + +-/* globals from run.c */ ++/* Globals from run.c */ + bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn, + CLST *len, size_t *index); + void run_truncate(struct runs_tree *run, CLST vcn); +@@ -753,13 +753,13 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + #endif + int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn); + +-/* globals from super.c */ ++/* Globals from super.c */ + void *ntfs_set_shared(void *ptr, u32 bytes); + void *ntfs_put_shared(void *ptr); + void ntfs_unmap_meta(struct super_block *sb, CLST lcn, CLST len); + int ntfs_discard(struct ntfs_sb_info *sbi, CLST Lcn, CLST Len); + +-/* globals from bitmap.c*/ ++/* Globals from bitmap.c*/ + int __init ntfs3_init_bitmap(void); + void ntfs3_exit_bitmap(void); + void wnd_close(struct wnd_bitmap *wnd); +@@ -773,7 +773,7 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits); + bool wnd_is_free(struct wnd_bitmap *wnd, size_t bit, size_t bits); + bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits); + +-/* Possible values for 'flags' 'wnd_find' */ ++/* Possible values for 'flags' 'wnd_find'. */ + #define BITMAP_FIND_MARK_AS_USED 0x01 + #define BITMAP_FIND_FULL 0x02 + size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, +@@ -782,7 +782,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits); + void wnd_zone_set(struct wnd_bitmap *wnd, size_t Lcn, size_t Len); + int ntfs_trim_fs(struct ntfs_sb_info *sbi, struct fstrim_range *range); + +-/* globals from upcase.c */ ++/* Globals from upcase.c */ + int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2, + const u16 *upcase, bool bothcase); + int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2, +@@ -822,7 +822,7 @@ static inline bool is_ntfs3(struct ntfs_sb_info *sbi) + return sbi->volume.major_ver >= 3; + } + +-/*(sb->s_flags & SB_ACTIVE)*/ ++/* (sb->s_flags & SB_ACTIVE) */ + static inline bool is_mounted(struct ntfs_sb_info *sbi) + { + return !!sbi->sb->s_root; +@@ -897,7 +897,7 @@ static inline bool run_is_empty(struct runs_tree *run) + return !run->count; + } + +-/* NTFS uses quad aligned bitmaps */ ++/* NTFS uses quad aligned bitmaps. */ + static inline size_t bitmap_size(size_t bits) + { + return ALIGN((bits + 7) >> 3, 8); +@@ -909,9 +909,7 @@ static inline size_t bitmap_size(size_t bits) + #define NTFS_TIME_GRAN 100 + + /* +- * kernel2nt +- * +- * converts in-memory kernel timestamp into nt time ++ * kernel2nt - Converts in-memory kernel timestamp into nt time. + */ + static inline __le64 kernel2nt(const struct timespec64 *ts) + { +@@ -922,9 +920,7 @@ static inline __le64 kernel2nt(const struct timespec64 *ts) + } + + /* +- * nt2kernel +- * +- * converts on-disk nt time into kernel timestamp ++ * nt2kernel - Converts on-disk nt time into kernel timestamp. + */ + static inline void nt2kernel(const __le64 tm, struct timespec64 *ts) + { +@@ -940,13 +936,17 @@ static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb) + return sb->s_fs_info; + } + +-/* Align up on cluster boundary */ ++/* ++ * ntfs_up_cluster - Align up on cluster boundary. ++ */ + static inline u64 ntfs_up_cluster(const struct ntfs_sb_info *sbi, u64 size) + { + return (size + sbi->cluster_mask) & sbi->cluster_mask_inv; + } + +-/* Align up on cluster boundary */ ++/* ++ * ntfs_up_block - Align up on cluster boundary. ++ */ + static inline u64 ntfs_up_block(const struct super_block *sb, u64 size) + { + return (size + sb->s_blocksize - 1) & ~(u64)(sb->s_blocksize - 1); +@@ -991,7 +991,7 @@ static inline int ni_ext_compress_bits(const struct ntfs_inode *ni) + return 0xb + (ni->ni_flags & NI_FLAG_COMPRESSED_MASK); + } + +-/* bits - 0xc, 0xd, 0xe, 0xf, 0x10 */ ++/* Bits - 0xc, 0xd, 0xe, 0xf, 0x10 */ + static inline void ni_set_ext_compress_bits(struct ntfs_inode *ni, u8 bits) + { + ni->ni_flags |= (bits - 0xb) & NI_FLAG_COMPRESSED_MASK; +diff --git a/src/record.c b/src/record.c +index dec83ac815c7983c1367cd451fd5985aa3d425c3..d48a5e6c5045c5f154f3c1aba4108f458e9676b0 100644 +--- a/src/record.c ++++ b/src/record.c +@@ -18,15 +18,13 @@ static inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, + const u16 *upcase) + { +- /* First, compare the type codes: */ ++ /* First, compare the type codes. */ + int diff = le32_to_cpu(left->type) - le32_to_cpu(type); + + if (diff) + return diff; + +- /* +- * They have the same type code, so we have to compare the names. +- */ ++ /* They have the same type code, so we have to compare the names. */ + return ntfs_cmp_names(attr_name(left), left->name_len, name, name_len, + upcase, true); + } +@@ -34,7 +32,7 @@ static inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type, + /* + * mi_new_attt_id + * +- * returns unused attribute id that is less than mrec->next_attr_id ++ * Return: Unused attribute id that is less than mrec->next_attr_id. + */ + static __le16 mi_new_attt_id(struct mft_inode *mi) + { +@@ -50,7 +48,7 @@ static __le16 mi_new_attt_id(struct mft_inode *mi) + return id; + } + +- /* One record can store up to 1024/24 ~= 42 attributes */ ++ /* One record can store up to 1024/24 ~= 42 attributes. */ + free_id = 0; + max_id = 0; + +@@ -115,9 +113,7 @@ int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno) + } + + /* +- * mi_read +- * +- * reads MFT data ++ * mi_read - Read MFT data. + */ + int mi_read(struct mft_inode *mi, bool is_mft) + { +@@ -178,7 +174,7 @@ int mi_read(struct mft_inode *mi, bool is_mft) + goto out; + + ok: +- /* check field 'total' only here */ ++ /* Check field 'total' only here. */ + if (le32_to_cpu(rec->total) != bpr) { + err = -EINVAL; + goto out; +@@ -210,13 +206,13 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + return NULL; + } + +- /* Skip non-resident records */ ++ /* Skip non-resident records. */ + if (!is_rec_inuse(rec)) + return NULL; + + attr = Add2Ptr(rec, off); + } else { +- /* Check if input attr inside record */ ++ /* Check if input attr inside record. */ + off = PtrOffset(rec, attr); + if (off >= used) + return NULL; +@@ -233,27 +229,27 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + + asize = le32_to_cpu(attr->size); + +- /* Can we use the first field (attr->type) */ ++ /* Can we use the first field (attr->type). */ + if (off + 8 > used) { + static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8); + return NULL; + } + + if (attr->type == ATTR_END) { +- /* end of enumeration */ ++ /* End of enumeration. */ + return NULL; + } + +- /* 0x100 is last known attribute for now*/ ++ /* 0x100 is last known attribute for now. */ + t32 = le32_to_cpu(attr->type); + if ((t32 & 0xf) || (t32 > 0x100)) + return NULL; + +- /* Check boundary */ ++ /* Check boundary. */ + if (off + asize > used) + return NULL; + +- /* Check size of attribute */ ++ /* Check size of attribute. */ + if (!attr->non_res) { + if (asize < SIZEOF_RESIDENT) + return NULL; +@@ -270,7 +266,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + return attr; + } + +- /* Check some nonresident fields */ ++ /* Check some nonresident fields. */ + if (attr->name_len && + le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len > + le16_to_cpu(attr->nres.run_off)) { +@@ -290,9 +286,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + } + + /* +- * mi_find_attr +- * +- * finds the attribute by type and name and id ++ * mi_find_attr - Find the attribute by type and name and id. + */ + struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr, + enum ATTR_TYPE type, const __le16 *name, +@@ -372,7 +366,7 @@ int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno, + } else if (mi_read(mi, is_mft)) { + ; + } else if (rec->rhdr.sign == NTFS_FILE_SIGNATURE) { +- /* Record is reused. Update its sequence number */ ++ /* Record is reused. Update its sequence number. */ + seq = le16_to_cpu(rec->seq) + 1; + if (!seq) + seq = 1; +@@ -404,9 +398,7 @@ int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno, + } + + /* +- * mi_mark_free +- * +- * marks record as unused and marks it as free in bitmap ++ * mi_mark_free - Mark record as unused and marks it as free in bitmap. + */ + void mi_mark_free(struct mft_inode *mi) + { +@@ -428,10 +420,9 @@ void mi_mark_free(struct mft_inode *mi) + } + + /* +- * mi_insert_attr ++ * mi_insert_attr - Reserve space for new attribute. + * +- * reserves space for new attribute +- * returns not full constructed attribute or NULL if not possible to create ++ * Return: Not full constructed attribute or NULL if not possible to create. + */ + struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, u32 asize, +@@ -468,7 +459,7 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, + } + + if (!attr) { +- tail = 8; /* not used, just to suppress warning */ ++ tail = 8; /* Not used, just to suppress warning. */ + attr = Add2Ptr(rec, used - 8); + } else { + tail = used - PtrOffset(rec, attr); +@@ -494,10 +485,9 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, + } + + /* +- * mi_remove_attr ++ * mi_remove_attr - Remove the attribute from record. + * +- * removes the attribute from record +- * NOTE: The source attr will point to next attribute ++ * NOTE: The source attr will point to next attribute. + */ + bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr) + { +@@ -543,7 +533,7 @@ bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes) + if (used + dsize > total) + return false; + nsize = asize + dsize; +- // move tail ++ /* Move tail */ + memmove(next + dsize, next, tail); + memset(next, 0, dsize); + used += dsize; +@@ -585,10 +575,10 @@ int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr, + u32 tail = used - aoff - asize; + u32 dsize = sbi->record_size - used; + +- /* Make a maximum gap in current record */ ++ /* Make a maximum gap in current record. */ + memmove(next + dsize, next, tail); + +- /* Pack as much as possible */ ++ /* Pack as much as possible. */ + err = run_pack(run, svcn, len, Add2Ptr(attr, run_off), run_size + dsize, + &plen); + if (err < 0) { +diff --git a/src/run.c b/src/run.c +index a9989f7536ba107621d15a2653183fb61fc45abc..26ed2b64345e665a39c6b37d88b29c5b81592ecd 100644 +--- a/src/run.c ++++ b/src/run.c +@@ -16,22 +16,21 @@ + #include "ntfs.h" + #include "ntfs_fs.h" + +-/* runs_tree is a continues memory. Try to avoid big size */ ++/* runs_tree is a continues memory. Try to avoid big size. */ + #define NTFS3_RUN_MAX_BYTES 0x10000 + + struct ntfs_run { +- CLST vcn; /* virtual cluster number */ +- CLST len; /* length in clusters */ +- CLST lcn; /* logical cluster number */ ++ CLST vcn; /* Virtual cluster number. */ ++ CLST len; /* Length in clusters. */ ++ CLST lcn; /* Logical cluster number. */ + }; + + /* +- * run_lookup ++ * run_lookup - Lookup the index of a MCB entry that is first <= vcn. + * +- * Lookup the index of a MCB entry that is first <= vcn. +- * case of success it will return non-zero value and set +- * 'index' parameter to index of entry been found. +- * case of entry missing from list 'index' will be set to ++ * Case of success it will return non-zero value and set ++ * @index parameter to index of entry been found. ++ * Case of entry missing from list 'index' will be set to + * point to insertion position for the entry question. + */ + bool run_lookup(const struct runs_tree *run, CLST vcn, size_t *index) +@@ -47,7 +46,7 @@ bool run_lookup(const struct runs_tree *run, CLST vcn, size_t *index) + min_idx = 0; + max_idx = run->count - 1; + +- /* Check boundary cases specially, 'cause they cover the often requests */ ++ /* Check boundary cases specially, 'cause they cover the often requests. */ + r = run->runs; + if (vcn < r->vcn) { + *index = 0; +@@ -91,9 +90,7 @@ bool run_lookup(const struct runs_tree *run, CLST vcn, size_t *index) + } + + /* +- * run_consolidate +- * +- * consolidate runs starting from a given one. ++ * run_consolidate - Consolidate runs starting from a given one. + */ + static void run_consolidate(struct runs_tree *run, size_t index) + { +@@ -164,7 +161,11 @@ static void run_consolidate(struct runs_tree *run, size_t index) + } + } + +-/* returns true if range [svcn - evcn] is mapped*/ ++/* ++ * run_is_mapped_full ++ * ++ * Return: True if range [svcn - evcn] is mapped. ++ */ + bool run_is_mapped_full(const struct runs_tree *run, CLST svcn, CLST evcn) + { + size_t i; +@@ -224,9 +225,7 @@ bool run_lookup_entry(const struct runs_tree *run, CLST vcn, CLST *lcn, + } + + /* +- * run_truncate_head +- * +- * decommit the range before vcn ++ * run_truncate_head - Decommit the range before vcn. + */ + void run_truncate_head(struct runs_tree *run, CLST vcn) + { +@@ -261,9 +260,7 @@ void run_truncate_head(struct runs_tree *run, CLST vcn) + } + + /* +- * run_truncate +- * +- * decommit the range after vcn ++ * run_truncate - Decommit the range after vcn. + */ + void run_truncate(struct runs_tree *run, CLST vcn) + { +@@ -285,13 +282,13 @@ void run_truncate(struct runs_tree *run, CLST vcn) + } + + /* +- * At this point 'index' is set to +- * position that should be thrown away (including index itself) ++ * At this point 'index' is set to position that ++ * should be thrown away (including index itself) + * Simple one - just set the limit. + */ + run->count = index; + +- /* Do not reallocate array 'runs'. Only free if possible */ ++ /* Do not reallocate array 'runs'. Only free if possible. */ + if (!index) { + kvfree(run->runs); + run->runs = NULL; +@@ -299,7 +296,9 @@ void run_truncate(struct runs_tree *run, CLST vcn) + } + } + +-/* trim head and tail if necessary*/ ++/* ++ * run_truncate_around - Trim head and tail if necessary. ++ */ + void run_truncate_around(struct runs_tree *run, CLST vcn) + { + run_truncate_head(run, vcn); +@@ -311,9 +310,10 @@ void run_truncate_around(struct runs_tree *run, CLST vcn) + /* + * run_add_entry + * +- * sets location to known state. +- * run to be added may overlap with existing location. +- * returns false if of memory ++ * Sets location to known state. ++ * Run to be added may overlap with existing location. ++ * ++ * Return: false if of memory. + */ + bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + bool is_mft) +@@ -336,7 +336,7 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + * Shortcut here would be case of + * range not been found but one been added + * continues previous run. +- * this case I can directly make use of ++ * This case I can directly make use of + * existing range as my start point. + */ + if (!inrange && index > 0) { +@@ -367,13 +367,13 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + /* + * Check allocated space. + * If one is not enough to get one more entry +- * then it will be reallocated ++ * then it will be reallocated. + */ + if (run->allocated < used + sizeof(struct ntfs_run)) { + size_t bytes; + struct ntfs_run *new_ptr; + +- /* Use power of 2 for 'bytes'*/ ++ /* Use power of 2 for 'bytes'. */ + if (!used) { + bytes = 64; + } else if (used <= 16 * PAGE_SIZE) { +@@ -421,10 +421,10 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + r = run->runs + index; + + /* +- * If one of ranges was not allocated +- * then I have to split location I just matched. +- * and insert current one +- * a common case this requires tail to be reinserted ++ * If one of ranges was not allocated then we ++ * have to split location we just matched and ++ * insert current one. ++ * A common case this requires tail to be reinserted + * a recursive call. + */ + if (((lcn == SPARSE_LCN) != (r->lcn == SPARSE_LCN)) || +@@ -449,12 +449,12 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + goto requires_new_range; + } + +- /* lcn should match one I'm going to add. */ ++ /* lcn should match one were going to add. */ + r->lcn = lcn; + } + + /* +- * If existing range fits then I'm done. ++ * If existing range fits then were done. + * Otherwise extend found one and fall back to range jocode. + */ + if (r->vcn + r->len < vcn + len) +@@ -473,8 +473,8 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + run_consolidate(run, index + 1); + + /* +- * a special case +- * I have to add extra range a tail. ++ * A special case. ++ * We have to add extra range a tail. + */ + if (should_add_tail && + !run_add_entry(run, tail_vcn, tail_lcn, tail_len, is_mft)) +@@ -483,7 +483,11 @@ bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len, + return true; + } + +-/*helper for attr_collapse_range, which is helper for fallocate(collapse_range)*/ ++/* run_collapse_range ++ * ++ * Helper for attr_collapse_range(), ++ * which is helper for fallocate(collapse_range). ++ */ + bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len) + { + size_t index, eat; +@@ -491,7 +495,7 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len) + CLST end; + + if (WARN_ON(!run_lookup(run, vcn, &index))) +- return true; /* should never be here */ ++ return true; /* Should never be here. */ + + e = run->runs + run->count; + r = run->runs + index; +@@ -499,13 +503,13 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len) + + if (vcn > r->vcn) { + if (r->vcn + r->len <= end) { +- /* collapse tail of run */ ++ /* Collapse tail of run .*/ + r->len = vcn - r->vcn; + } else if (r->lcn == SPARSE_LCN) { +- /* collapse a middle part of sparsed run */ ++ /* Collapse a middle part of sparsed run. */ + r->len -= len; + } else { +- /* collapse a middle part of normal run, split */ ++ /* Collapse a middle part of normal run, split. */ + if (!run_add_entry(run, vcn, SPARSE_LCN, len, false)) + return false; + return run_collapse_range(run, vcn, len); +@@ -526,7 +530,7 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len) + } + + if (r->vcn + r->len <= end) { +- /* eat this run */ ++ /* Eat this run. */ + eat_end = r + 1; + continue; + } +@@ -546,9 +550,7 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len) + } + + /* +- * run_get_entry +- * +- * returns index-th mapped region ++ * run_get_entry - Return index-th mapped region. + */ + bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn, + CLST *lcn, CLST *len) +@@ -573,9 +575,7 @@ bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn, + } + + /* +- * run_packed_size +- * +- * calculates the size of packed int64 ++ * run_packed_size - Calculate the size of packed int64. + */ + #ifdef __BIG_ENDIAN + static inline int run_packed_size(const s64 n) +@@ -605,7 +605,7 @@ static inline int run_packed_size(const s64 n) + return (const u8 *)&n + sizeof(n) - p; + } + +-/* full trusted function. It does not check 'size' for errors */ ++/* Full trusted function. It does not check 'size' for errors. */ + static inline void run_pack_s64(u8 *run_buf, u8 size, s64 v) + { + const u8 *p = (u8 *)&v; +@@ -637,7 +637,7 @@ static inline void run_pack_s64(u8 *run_buf, u8 size, s64 v) + } + } + +-/* full trusted function. It does not check 'size' for errors */ ++/* Full trusted function. It does not check 'size' for errors. */ + static inline s64 run_unpack_s64(const u8 *run_buf, u8 size, s64 v) + { + u8 *p = (u8 *)&v; +@@ -700,12 +700,12 @@ static inline int run_packed_size(const s64 n) + return 1 + p - (const u8 *)&n; + } + +-/* full trusted function. It does not check 'size' for errors */ ++/* Full trusted function. It does not check 'size' for errors. */ + static inline void run_pack_s64(u8 *run_buf, u8 size, s64 v) + { + const u8 *p = (u8 *)&v; + +- /* memcpy( run_buf, &v, size); is it faster? */ ++ /* memcpy( run_buf, &v, size); Is it faster? */ + switch (size) { + case 8: + run_buf[7] = p[7]; +@@ -738,7 +738,7 @@ static inline s64 run_unpack_s64(const u8 *run_buf, u8 size, s64 v) + { + u8 *p = (u8 *)&v; + +- /* memcpy( &v, run_buf, size); is it faster? */ ++ /* memcpy( &v, run_buf, size); Is it faster? */ + switch (size) { + case 8: + p[7] = run_buf[7]; +@@ -769,11 +769,10 @@ static inline s64 run_unpack_s64(const u8 *run_buf, u8 size, s64 v) + #endif + + /* +- * run_pack ++ * run_pack - Pack runs into buffer. + * +- * packs runs into buffer +- * packed_vcns - how much runs we have packed +- * packed_size - how much bytes we have used run_buf ++ * packed_vcns - How much runs we have packed. ++ * packed_size - How much bytes we have used run_buf. + */ + int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, + u32 run_buf_size, CLST *packed_vcns) +@@ -807,10 +806,10 @@ int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, + if (next_vcn > evcn1) + len = evcn1 - vcn; + +- /* how much bytes required to pack len */ ++ /* How much bytes required to pack len. */ + size_size = run_packed_size(len); + +- /* offset_size - how much bytes is packed dlcn */ ++ /* offset_size - How much bytes is packed dlcn. */ + if (lcn == SPARSE_LCN) { + offset_size = 0; + dlcn = 0; +@@ -825,20 +824,20 @@ int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, + if (tmp <= 0) + goto out; + +- /* can we store this entire run */ ++ /* Can we store this entire run. */ + if (tmp < size_size) + goto out; + + if (run_buf) { +- /* pack run header */ ++ /* Pack run header. */ + run_buf[0] = ((u8)(size_size | (offset_size << 4))); + run_buf += 1; + +- /* Pack the length of run */ ++ /* Pack the length of run. */ + run_pack_s64(run_buf, size_size, len); + + run_buf += size_size; +- /* Pack the offset from previous lcn */ ++ /* Pack the offset from previous LCN. */ + run_pack_s64(run_buf, offset_size, dlcn); + run_buf += offset_size; + } +@@ -858,7 +857,7 @@ int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, + } + + out: +- /* Store last zero */ ++ /* Store last zero. */ + if (run_buf) + run_buf[0] = 0; + +@@ -869,10 +868,9 @@ int run_pack(const struct runs_tree *run, CLST svcn, CLST len, u8 *run_buf, + } + + /* +- * run_unpack ++ * run_unpack - Unpack packed runs from @run_buf. + * +- * unpacks packed runs from "run_buf" +- * returns error, if negative, or real used bytes ++ * Return: Error if negative, or real used bytes. + */ + int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf, +@@ -882,7 +880,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + const u8 *run_last, *run_0; + bool is_mft = ino == MFT_REC_MFT; + +- /* Check for empty */ ++ /* Check for empty. */ + if (evcn + 1 == svcn) + return 0; + +@@ -894,12 +892,12 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + prev_lcn = 0; + vcn64 = svcn; + +- /* Read all runs the chain */ +- /* size_size - how much bytes is packed len */ ++ /* Read all runs the chain. */ ++ /* size_size - How much bytes is packed len. */ + while (run_buf < run_last) { +- /* size_size - how much bytes is packed len */ ++ /* size_size - How much bytes is packed len. */ + u8 size_size = *run_buf & 0xF; +- /* offset_size - how much bytes is packed dlcn */ ++ /* offset_size - How much bytes is packed dlcn. */ + u8 offset_size = *run_buf++ >> 4; + u64 len; + +@@ -908,8 +906,8 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + + /* + * Unpack runs. +- * NOTE: runs are stored little endian order +- * "len" is unsigned value, "dlcn" is signed ++ * NOTE: Runs are stored little endian order ++ * "len" is unsigned value, "dlcn" is signed. + * Large positive number requires to store 5 bytes + * e.g.: 05 FF 7E FF FF 00 00 00 + */ +@@ -917,7 +915,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + return -EINVAL; + + len = run_unpack_s64(run_buf, size_size, 0); +- /* skip size_size */ ++ /* Skip size_size. */ + run_buf += size_size; + + if (!len) +@@ -928,10 +926,10 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + else if (offset_size <= 8) { + s64 dlcn; + +- /* initial value of dlcn is -1 or 0 */ ++ /* Initial value of dlcn is -1 or 0. */ + dlcn = (run_buf[offset_size - 1] & 0x80) ? (s64)-1 : 0; + dlcn = run_unpack_s64(run_buf, offset_size, dlcn); +- /* skip offset_size */ ++ /* Skip offset_size. */ + run_buf += offset_size; + + if (!dlcn) +@@ -942,7 +940,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + return -EINVAL; + + next_vcn = vcn64 + len; +- /* check boundary */ ++ /* Check boundary. */ + if (next_vcn > evcn + 1) + return -EINVAL; + +@@ -958,14 +956,17 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + } + #endif + if (lcn != SPARSE_LCN64 && lcn + len > sbi->used.bitmap.nbits) { +- /* lcn range is out of volume */ ++ /* LCN range is out of volume. */ + return -EINVAL; + } + + if (!run) +- ; /* called from check_attr(fslog.c) to check run */ ++ ; /* Called from check_attr(fslog.c) to check run. */ + else if (run == RUN_DEALLOCATE) { +- /* called from ni_delete_all to free clusters without storing in run */ ++ /* ++ * Called from ni_delete_all to free clusters ++ * without storing in run. ++ */ + if (lcn != SPARSE_LCN64) + mark_as_free_ex(sbi, lcn, len, true); + } else if (vcn64 >= vcn) { +@@ -983,7 +984,7 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + } + + if (vcn64 != evcn + 1) { +- /* not expected length of unpacked runs */ ++ /* Not expected length of unpacked runs. */ + return -EINVAL; + } + +@@ -992,11 +993,11 @@ int run_unpack(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + + #ifdef NTFS3_CHECK_FREE_CLST + /* +- * run_unpack_ex ++ * run_unpack_ex - Unpack packed runs from "run_buf". ++ * ++ * Checks unpacked runs to be used in bitmap. + * +- * unpacks packed runs from "run_buf" +- * checks unpacked runs to be used in bitmap +- * returns error, if negative, or real used bytes ++ * Return: Error if negative, or real used bytes. + */ + int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + CLST svcn, CLST evcn, CLST vcn, const u8 *run_buf, +@@ -1036,17 +1037,17 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + continue; + + down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); +- /* Check for free blocks */ ++ /* Check for free blocks. */ + ok = wnd_is_used(wnd, lcn, len); + up_read(&wnd->rw_lock); + if (ok) + continue; + +- /* Looks like volume is corrupted */ ++ /* Looks like volume is corrupted. */ + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + + if (down_write_trylock(&wnd->rw_lock)) { +- /* mark all zero bits as used in range [lcn, lcn+len) */ ++ /* Mark all zero bits as used in range [lcn, lcn+len). */ + CLST i, lcn_f = 0, len_f = 0; + + err = 0; +@@ -1079,8 +1080,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, + /* + * run_get_highest_vcn + * +- * returns the highest vcn from a mapping pairs array +- * it used while replaying log file ++ * Return the highest vcn from a mapping pairs array ++ * it used while replaying log file. + */ + int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) + { +diff --git a/src/super.c b/src/super.c +index 17ee715ab539ee6d7605256277c1f77c3132a6fa..2fbab8a931eeb7a3c69471fb1fcb1c3e8b0d582a 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -7,15 +7,15 @@ + * terminology + * + * cluster - allocation unit - 512,1K,2K,4K,...,2M +- * vcn - virtual cluster number - offset inside the file in clusters +- * vbo - virtual byte offset - offset inside the file in bytes +- * lcn - logical cluster number - 0 based cluster in clusters heap +- * lbo - logical byte offset - absolute position inside volume +- * run - maps vcn to lcn - stored in attributes in packed form +- * attr - attribute segment - std/name/data etc records inside MFT +- * mi - mft inode - one MFT record(usually 1024 bytes or 4K), consists of attributes +- * ni - ntfs inode - extends linux inode. consists of one or more mft inodes +- * index - unit inside directory - 2K, 4K, <=page size, does not depend on cluster size ++ * vcn - virtual cluster number - Offset inside the file in clusters. ++ * vbo - virtual byte offset - Offset inside the file in bytes. ++ * lcn - logical cluster number - 0 based cluster in clusters heap. ++ * lbo - logical byte offset - Absolute position inside volume. ++ * run - maps VCN to LCN - Stored in attributes in packed form. ++ * attr - attribute segment - std/name/data etc records inside MFT. ++ * mi - MFT inode - One MFT record(usually 1024 bytes or 4K), consists of attributes. ++ * ni - NTFS inode - Extends linux inode. consists of one or more mft inodes. ++ * index - unit inside directory - 2K, 4K, <=page size, does not depend on cluster size. + * + * WSL - Windows Subsystem for Linux + * https://docs.microsoft.com/en-us/windows/wsl/file-permissions +@@ -45,7 +45,8 @@ + + #ifdef CONFIG_PRINTK + /* +- * Trace warnings/notices/errors ++ * ntfs_printk - Trace warnings/notices/errors. ++ * + * Thanks Joe Perches for implementation + */ + void ntfs_printk(const struct super_block *sb, const char *fmt, ...) +@@ -55,7 +56,7 @@ void ntfs_printk(const struct super_block *sb, const char *fmt, ...) + int level; + struct ntfs_sb_info *sbi = sb->s_fs_info; + +- /*should we use different ratelimits for warnings/notices/errors? */ ++ /* Should we use different ratelimits for warnings/notices/errors? */ + if (!___ratelimit(&sbi->msg_ratelimit, "ntfs3")) + return; + +@@ -70,9 +71,13 @@ void ntfs_printk(const struct super_block *sb, const char *fmt, ...) + } + + static char s_name_buf[512]; +-static atomic_t s_name_buf_cnt = ATOMIC_INIT(1); // 1 means 'free s_name_buf' ++static atomic_t s_name_buf_cnt = ATOMIC_INIT(1); // 1 means 'free s_name_buf'. + +-/* print warnings/notices/errors about inode using name or inode number */ ++/* ++ * ntfs_inode_printk ++ * ++ * Print warnings/notices/errors about inode using name or inode number. ++ */ + void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + { + struct super_block *sb = inode->i_sb; +@@ -85,7 +90,7 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + if (!___ratelimit(&sbi->msg_ratelimit, "ntfs3")) + return; + +- /* use static allocated buffer, if possible */ ++ /* Use static allocated buffer, if possible. */ + name = atomic_dec_and_test(&s_name_buf_cnt) + ? s_name_buf + : kmalloc(sizeof(s_name_buf), GFP_NOFS); +@@ -98,11 +103,11 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + spin_lock(&de->d_lock); + snprintf(name, name_len, " \"%s\"", de->d_name.name); + spin_unlock(&de->d_lock); +- name[name_len] = 0; /* to be sure*/ ++ name[name_len] = 0; /* To be sure. */ + } else { + name[0] = 0; + } +- dput(de); /* cocci warns if placed in branch "if (de)" */ ++ dput(de); /* Cocci warns if placed in branch "if (de)" */ + } + + va_start(args, fmt); +@@ -125,12 +130,12 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...) + /* + * Shared memory struct. + * +- * on-disk ntfs's upcase table is created by ntfs formatter +- * 'upcase' table is 128K bytes of memory +- * we should read it into memory when mounting +- * Several ntfs volumes likely use the same 'upcase' table +- * It is good idea to share in-memory 'upcase' table between different volumes +- * Unfortunately winxp/vista/win7 use different upcase tables ++ * On-disk ntfs's upcase table is created by ntfs formatter. ++ * 'upcase' table is 128K bytes of memory. ++ * We should read it into memory when mounting. ++ * Several ntfs volumes likely use the same 'upcase' table. ++ * It is good idea to share in-memory 'upcase' table between different volumes. ++ * Unfortunately winxp/vista/win7 use different upcase tables. + */ + static DEFINE_SPINLOCK(s_shared_lock); + +@@ -143,8 +148,9 @@ static struct { + /* + * ntfs_set_shared + * +- * Returns 'ptr' if pointer was saved in shared memory +- * Returns NULL if pointer was not shared ++ * Return: ++ * * @ptr - If pointer was saved in shared memory. ++ * * NULL - If pointer was not shared. + */ + void *ntfs_set_shared(void *ptr, u32 bytes) + { +@@ -177,8 +183,9 @@ void *ntfs_set_shared(void *ptr, u32 bytes) + /* + * ntfs_put_shared + * +- * Returns 'ptr' if pointer is not shared anymore +- * Returns NULL if pointer is still shared ++ * Return: ++ * * @ptr - If pointer is not shared anymore. ++ * * NULL - If pointer is still shared. + */ + void *ntfs_put_shared(void *ptr) + { +@@ -353,7 +360,10 @@ static noinline int ntfs_parse_options(struct super_block *sb, char *options, + + out: + if (!strcmp(nls_name[0] ? nls_name : CONFIG_NLS_DEFAULT, "utf8")) { +- /* For UTF-8 use utf16s_to_utf8s/utf8s_to_utf16s instead of nls */ ++ /* ++ * For UTF-8 use utf16s_to_utf8s()/utf8s_to_utf16s() ++ * instead of NLS. ++ */ + nls = NULL; + } else if (nls_name[0]) { + nls = load_nls(nls_name); +@@ -383,7 +393,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *data) + if (data && !orig_data) + return -ENOMEM; + +- /* Store original options */ ++ /* Store original options. */ + memcpy(&old_opts, &sbi->options, sizeof(old_opts)); + clear_mount_options(&sbi->options); + memset(&sbi->options, 0, sizeof(sbi->options)); +@@ -465,7 +475,9 @@ static void init_once(void *foo) + inode_init_once(&ni->vfs_inode); + } + +-/* noinline to reduce binary size*/ ++/* ++ * put_ntfs - Noinline to reduce binary size. ++ */ + static noinline void put_ntfs(struct ntfs_sb_info *sbi) + { + kfree(sbi->new_rec); +@@ -510,7 +522,7 @@ static void ntfs_put_super(struct super_block *sb) + { + struct ntfs_sb_info *sbi = sb->s_fs_info; + +- /*mark rw ntfs as clear, if possible*/ ++ /* Mark rw ntfs as clear, if possible. */ + ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); + + put_ntfs(sbi); +@@ -581,7 +593,9 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + return 0; + } + +-/*super_operations::sync_fs*/ ++/* ++ * ntfs_sync_fs - super_operations::sync_fs ++ */ + static int ntfs_sync_fs(struct super_block *sb, int wait) + { + int err = 0, err2; +@@ -683,10 +697,12 @@ static const struct export_operations ntfs_export_ops = { + .commit_metadata = ntfs_nfs_commit_metadata, + }; + +-/* Returns Gb,Mb to print with "%u.%02u Gb" */ ++/* ++ * format_size_gb - Return Gb,Mb to print with "%u.%02u Gb". ++ */ + static u32 format_size_gb(const u64 bytes, u32 *mb) + { +- /* Do simple right 30 bit shift of 64 bit value */ ++ /* Do simple right 30 bit shift of 64 bit value. */ + u64 kbytes = bytes >> 10; + u32 kbytes32 = kbytes; + +@@ -704,7 +720,9 @@ static u32 true_sectors_per_clst(const struct NTFS_BOOT *boot) + : (1u << (0 - boot->sectors_per_clusters)); + } + +-/* inits internal info from on-disk boot sector*/ ++/* ++ * ntfs_init_from_boot - Init internal info from on-disk boot sector. ++ */ + static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + u64 dev_size) + { +@@ -755,14 +773,14 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + if (mlcn2 * sct_per_clst >= sectors) + goto out; + +- /* Check MFT record size */ ++ /* Check MFT record size. */ + if ((boot->record_size < 0 && + SECTOR_SIZE > (2U << (-boot->record_size))) || + (boot->record_size >= 0 && !is_power_of_2(boot->record_size))) { + goto out; + } + +- /* Check index record size */ ++ /* Check index record size. */ + if ((boot->index_size < 0 && + SECTOR_SIZE > (2U << (-boot->index_size))) || + (boot->index_size >= 0 && !is_power_of_2(boot->index_size))) { +@@ -776,9 +794,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + gb = format_size_gb(fs_size, &mb); + + /* +- * - Volume formatted and mounted with the same sector size +- * - Volume formatted 4K and mounted as 512 +- * - Volume formatted 512 and mounted as 4K ++ * - Volume formatted and mounted with the same sector size. ++ * - Volume formatted 4K and mounted as 512. ++ * - Volume formatted 512 and mounted as 4K. + */ + if (sbi->sector_size != sector_size) { + ntfs_warn(sb, +@@ -820,7 +838,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + sbi->volume.ser_num = le64_to_cpu(boot->serial_num); + sbi->volume.size = sectors << sbi->sector_bits; + +- /* warning if RAW volume */ ++ /* Warning if RAW volume. */ + if (dev_size < fs_size) { + u32 mb0, gb0; + +@@ -834,7 +852,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + + clusters = sbi->volume.size >> sbi->cluster_bits; + #ifndef CONFIG_NTFS3_64BIT_CLUSTER +- /* 32 bits per cluster */ ++ /* 32 bits per cluster. */ + if (clusters >> 32) { + ntfs_notice( + sb, +@@ -872,7 +890,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + sbi->blocks_per_cluster = sbi->cluster_size >> sb->s_blocksize_bits; + sbi->volume.blocks = sbi->volume.size >> sb->s_blocksize_bits; + +- /* Maximum size for normal files */ ++ /* Maximum size for normal files. */ + sbi->maxbytes = (clusters << sbi->cluster_bits) - 1; + + #ifdef CONFIG_NTFS3_64BIT_CLUSTER +@@ -880,7 +898,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + sbi->maxbytes = -1; + sbi->maxbytes_sparse = -1; + #else +- /* Maximum size for sparse file */ ++ /* Maximum size for sparse file. */ + sbi->maxbytes_sparse = (1ull << (sbi->cluster_bits + 32)) - 1; + #endif + +@@ -892,7 +910,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + return err; + } + +-/* try to mount*/ ++/* ++ * ntfs_fill_super - Try to mount. ++ */ + static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + { + int err; +@@ -945,7 +965,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + sb_set_blocksize(sb, PAGE_SIZE); + +- /* parse boot */ ++ /* Parse boot. */ + err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512, + bd_inode->i_size); + if (err) +@@ -964,8 +984,8 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + #endif + + /* +- * Load $Volume. This should be done before LogFile +- * 'cause 'sbi->volume.ni' is used 'ntfs_set_state' ++ * Load $Volume. This should be done before $LogFile ++ * 'cause 'sbi->volume.ni' is used 'ntfs_set_state'. + */ + ref.low = cpu_to_le32(MFT_REC_VOL); + ref.seq = cpu_to_le16(MFT_REC_VOL); +@@ -979,13 +999,13 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + ni = ntfs_i(inode); + +- /* Load and save label (not necessary) */ ++ /* Load and save label (not necessary). */ + attr = ni_find_attr(ni, NULL, NULL, ATTR_LABEL, NULL, 0, NULL, NULL); + + if (!attr) { + /* It is ok if no ATTR_LABEL */ + } else if (!attr->non_res && !is_attr_ext(attr)) { +- /* $AttrDef allows labels to be up to 128 symbols */ ++ /* $AttrDef allows labels to be up to 128 symbols. */ + err = utf16s_to_utf8s(resident_data(attr), + le32_to_cpu(attr->res.data_size) >> 1, + UTF16_LITTLE_ENDIAN, sbi->volume.label, +@@ -993,7 +1013,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + if (err < 0) + sbi->volume.label[0] = 0; + } else { +- /* should we break mounting here? */ ++ /* Should we break mounting here? */ + //err = -EINVAL; + //goto out; + } +@@ -1017,7 +1037,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + sbi->volume.ni = ni; + inode = NULL; + +- /* Load $MFTMirr to estimate recs_mirr */ ++ /* Load $MFTMirr to estimate recs_mirr. */ + ref.low = cpu_to_le32(MFT_REC_MIRR); + ref.seq = cpu_to_le16(MFT_REC_MIRR); + inode = ntfs_iget5(sb, &ref, &NAME_MIRROR); +@@ -1033,7 +1053,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + iput(inode); + +- /* Load LogFile to replay */ ++ /* Load $LogFile to replay. */ + ref.low = cpu_to_le32(MFT_REC_LOG); + ref.seq = cpu_to_le16(MFT_REC_LOG); + inode = ntfs_iget5(sb, &ref, &NAME_LOGFILE); +@@ -1072,7 +1092,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + } + } + +- /* Load $MFT */ ++ /* Load $MFT. */ + ref.low = cpu_to_le32(MFT_REC_MFT); + ref.seq = cpu_to_le16(1); + +@@ -1100,7 +1120,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + sbi->mft.ni = ni; + +- /* Load $BadClus */ ++ /* Load $BadClus. */ + ref.low = cpu_to_le32(MFT_REC_BADCLUST); + ref.seq = cpu_to_le16(MFT_REC_BADCLUST); + inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS); +@@ -1125,7 +1145,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + iput(inode); + +- /* Load $Bitmap */ ++ /* Load $Bitmap. */ + ref.low = cpu_to_le32(MFT_REC_BITMAP); + ref.seq = cpu_to_le16(MFT_REC_BITMAP); + inode = ntfs_iget5(sb, &ref, &NAME_BITMAP); +@@ -1145,14 +1165,14 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + } + #endif + +- /* Check bitmap boundary */ ++ /* Check bitmap boundary. */ + tt = sbi->used.bitmap.nbits; + if (inode->i_size < bitmap_size(tt)) { + err = -EINVAL; + goto out; + } + +- /* Not necessary */ ++ /* Not necessary. */ + sbi->used.bitmap.set_tail = true; + err = wnd_init(&sbi->used.bitmap, sbi->sb, tt); + if (err) +@@ -1160,12 +1180,12 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + iput(inode); + +- /* Compute the mft zone */ ++ /* Compute the MFT zone. */ + err = ntfs_refresh_zone(sbi); + if (err) + goto out; + +- /* Load $AttrDef */ ++ /* Load $AttrDef. */ + ref.low = cpu_to_le32(MFT_REC_ATTR); + ref.seq = cpu_to_le16(MFT_REC_ATTR); + inode = ntfs_iget5(sbi->sb, &ref, &NAME_ATTRDEF); +@@ -1229,7 +1249,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + } + iput(inode); + +- /* Load $UpCase */ ++ /* Load $UpCase. */ + ref.low = cpu_to_le32(MFT_REC_UPCASE); + ref.seq = cpu_to_le16(MFT_REC_UPCASE); + inode = ntfs_iget5(sb, &ref, &NAME_UPCASE); +@@ -1284,29 +1304,29 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + inode = NULL; + + if (is_ntfs3(sbi)) { +- /* Load $Secure */ ++ /* Load $Secure. */ + err = ntfs_security_init(sbi); + if (err) + goto out; + +- /* Load $Extend */ ++ /* Load $Extend. */ + err = ntfs_extend_init(sbi); + if (err) + goto load_root; + +- /* Load $Extend\$Reparse */ ++ /* Load $Extend\$Reparse. */ + err = ntfs_reparse_init(sbi); + if (err) + goto load_root; + +- /* Load $Extend\$ObjId */ ++ /* Load $Extend\$ObjId. */ + err = ntfs_objid_init(sbi); + if (err) + goto load_root; + } + + load_root: +- /* Load root */ ++ /* Load root. */ + ref.low = cpu_to_le32(MFT_REC_ROOT); + ref.seq = cpu_to_le16(MFT_REC_ROOT); + inode = ntfs_iget5(sb, &ref, &NAME_ROOT); +@@ -1369,9 +1389,7 @@ void ntfs_unmap_meta(struct super_block *sb, CLST lcn, CLST len) + } + + /* +- * ntfs_discard +- * +- * issue a discard request (trim for SSD) ++ * ntfs_discard - Issue a discard request (trim for SSD). + */ + int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len) + { +@@ -1391,10 +1409,10 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len) + lbo = (u64)lcn << sbi->cluster_bits; + bytes = (u64)len << sbi->cluster_bits; + +- /* Align up 'start' on discard_granularity */ ++ /* Align up 'start' on discard_granularity. */ + start = (lbo + sbi->discard_granularity - 1) & + sbi->discard_granularity_mask_inv; +- /* Align down 'end' on discard_granularity */ ++ /* Align down 'end' on discard_granularity. */ + end = (lbo + bytes) & sbi->discard_granularity_mask_inv; + + sb = sbi->sb; +@@ -1443,7 +1461,7 @@ static int __init init_ntfs_fs(void) + pr_notice("ntfs3: Activated 32 bits per cluster\n"); + #endif + #ifdef CONFIG_NTFS3_LZX_XPRESS +- pr_notice("ntfs3: Read-only lzx/xpress compression included\n"); ++ pr_notice("ntfs3: Read-only LZX/Xpress compression included\n"); + #endif + + err = ntfs3_init_bitmap(); +diff --git a/src/upcase.c b/src/upcase.c +index b53943538f9f2525764371ca0d925da295dbb10a..eb65bbd939e8cdf17d209b60243e5fddeddabdf3 100644 +--- a/src/upcase.c ++++ b/src/upcase.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. + * + */ ++ + #include + #include + #include +@@ -25,14 +26,16 @@ static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr) + } + + /* ++ * ntfs_cmp_names ++ * + * Thanks Kari Argillander for idea and implementation 'bothcase' + * + * Straight way to compare names: +- * - case insensitive +- * - if name equals and 'bothcases' then +- * - case sensitive +- * 'Straigth way' code scans input names twice in worst case +- * Optimized code scans input names only once ++ * - Case insensitive ++ * - If name equals and 'bothcases' then ++ * - Case sensitive ++ * 'Straigth way' code scans input names twice in worst case. ++ * Optimized code scans input names only once. + */ + int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2, + const u16 *upcase, bool bothcase) +diff --git a/src/xattr.c b/src/xattr.c +index d3d5b9d331d1f55010843085738cf395b640eb0b..b4c921e4bc1a1b4bbb1c51ef7cef6677967b8e13 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -41,7 +41,7 @@ static inline size_t packed_ea_size(const struct EA_FULL *ea) + /* + * find_ea + * +- * assume there is at least one xattr in the list ++ * Assume there is at least one xattr in the list. + */ + static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes, + const char *name, u8 name_len, u32 *off) +@@ -69,11 +69,9 @@ static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes, + } + + /* +- * ntfs_read_ea +- * +- * reads all extended attributes +- * ea - new allocated memory +- * info - pointer into resident data ++ * ntfs_read_ea - Read all extended attributes. ++ * @ea: New allocated memory. ++ * @info: Pointer into resident data. + */ + static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + size_t add_bytes, const struct EA_INFO **info) +@@ -101,7 +99,7 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + if (!*info) + return -EINVAL; + +- /* Check Ea limit */ ++ /* Check Ea limit. */ + size = le32_to_cpu((*info)->size); + if (size > ni->mi.sbi->ea_max_size) + return -EFBIG; +@@ -109,7 +107,7 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + if (attr_size(attr_ea) > ni->mi.sbi->ea_max_size) + return -EFBIG; + +- /* Allocate memory for packed Ea */ ++ /* Allocate memory for packed Ea. */ + ea_p = kmalloc(size + add_bytes, GFP_NOFS); + if (!ea_p) + return -ENOMEM; +@@ -150,11 +148,12 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + /* + * ntfs_list_ea + * +- * copy a list of xattrs names into the buffer +- * provided, or compute the buffer size required ++ * Copy a list of xattrs names into the buffer ++ * provided, or compute the buffer size required. + * +- * Returns a negative error number on failure, or the number of bytes +- * used / required on success. ++ * Return: ++ * * Number of bytes used / required on ++ * * -ERRNO - on failure + */ + static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, + size_t bytes_per_buffer) +@@ -175,7 +174,7 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer, + + size = le32_to_cpu(info->size); + +- /* Enumerate all xattrs */ ++ /* Enumerate all xattrs. */ + for (ret = 0, off = 0; off < size; off += unpacked_ea_size(ea)) { + ea = Add2Ptr(ea_all, off); + +@@ -227,7 +226,7 @@ static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, + if (!info) + goto out; + +- /* Enumerate all xattrs */ ++ /* Enumerate all xattrs. */ + if (!find_ea(ea_all, le32_to_cpu(info->size), name, name_len, &off)) { + err = -ENODATA; + goto out; +@@ -322,11 +321,11 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + */ + if (val_size && le16_to_cpu(ea->elength) == val_size && + !memcmp(ea->name + ea->name_len + 1, value, val_size)) { +- /* xattr already contains the required value */ ++ /* xattr already contains the required value. */ + goto out; + } + +- /* Remove current xattr */ ++ /* Remove current xattr. */ + if (ea->flags & FILE_NEED_EA) + le16_add_cpu(&ea_info.count, -1); + +@@ -342,7 +341,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + ea_info.size = cpu_to_le32(size); + + if ((flags & XATTR_REPLACE) && !val_size) { +- /* remove xattr */ ++ /* Remove xattr. */ + goto update_ea; + } + } else { +@@ -360,7 +359,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + } + } + +- /* append new xattr */ ++ /* Append new xattr. */ + new_ea = Add2Ptr(ea_all, size); + new_ea->size = cpu_to_le32(add); + new_ea->flags = 0; +@@ -371,14 +370,14 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + memcpy(new_ea->name + name_len + 1, value, val_size); + new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea); + +- /* should fit into 16 bits */ ++ /* Should fit into 16 bits. */ + if (new_pack > 0xffff) { + err = -EFBIG; // -EINVAL? + goto out; + } + ea_info.size_pack = cpu_to_le16(new_pack); + +- /* new size of ATTR_EA */ ++ /* New size of ATTR_EA. */ + size += add; + if (size > sbi->ea_max_size) { + err = -EFBIG; // -EINVAL? +@@ -389,7 +388,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + update_ea: + + if (!info) { +- /* Create xattr */ ++ /* Create xattr. */ + if (!size) { + err = 0; + goto out; +@@ -419,7 +418,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + } + + if (!size) { +- /* delete xattr, ATTR_EA_INFO */ ++ /* Delete xattr, ATTR_EA_INFO */ + err = ni_remove_attr_le(ni, attr, le); + if (err) + goto out; +@@ -441,7 +440,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + } + + if (!size) { +- /* delete xattr, ATTR_EA */ ++ /* Delete xattr, ATTR_EA */ + err = ni_remove_attr_le(ni, attr, le); + if (err) + goto out; +@@ -459,7 +458,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + mi->dirty = true; + } + +- /* Check if we delete the last xattr */ ++ /* Check if we delete the last xattr. */ + if (size) + ni->ni_flags |= NI_FLAG_EA; + else +@@ -498,12 +497,12 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + int err; + void *buf; + +- /* allocate PATH_MAX bytes */ ++ /* Allocate PATH_MAX bytes. */ + buf = __getname(); + if (!buf) + return ERR_PTR(-ENOMEM); + +- /* Possible values of 'type' was already checked above */ ++ /* Possible values of 'type' was already checked above. */ + if (type == ACL_TYPE_ACCESS) { + name = XATTR_NAME_POSIX_ACL_ACCESS; + name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; +@@ -520,7 +519,7 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + if (!locked) + ni_unlock(ni); + +- /* Translate extended attribute to acl */ ++ /* Translate extended attribute to acl. */ + if (err >= 0) { + acl = posix_acl_from_xattr(mnt_userns, buf, err); + if (!IS_ERR(acl)) +@@ -535,9 +534,7 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + } + + /* +- * ntfs_get_acl +- * +- * inode_operations::get_acl ++ * ntfs_get_acl - inode_operations::get_acl + */ + struct posix_acl *ntfs_get_acl(struct inode *inode, int type) + { +@@ -573,8 +570,8 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + + if (!err) { + /* +- * acl can be exactly represented in the +- * traditional file mode permission bits ++ * ACL can be exactly represented in the ++ * traditional file mode permission bits. + */ + acl = NULL; + } +@@ -620,9 +617,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + } + + /* +- * ntfs_set_acl +- * +- * inode_operations::set_acl ++ * ntfs_set_acl - inode_operations::set_acl + */ + int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct posix_acl *acl, int type) +@@ -688,7 +683,9 @@ static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns, + } + + /* +- * Initialize the ACLs of a new inode. Called from ntfs_create_inode. ++ * ntfs_init_acl - Initialize the ACLs of a new inode. ++ * ++ * Called from ntfs_create_inode(). + */ + int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct inode *dir) +@@ -697,7 +694,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + int err; + + /* +- * TODO refactoring lock ++ * TODO: Refactoring lock. + * ni_lock(dir) ... -> posix_acl_create(dir,...) -> ntfs_get_acl -> ni_lock(dir) + */ + inode->i_default_acl = NULL; +@@ -749,9 +746,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + #endif + + /* +- * ntfs_acl_chmod +- * +- * helper for 'ntfs3_setattr' ++ * ntfs_acl_chmod - Helper for ntfs3_setattr(). + */ + int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) + { +@@ -767,15 +762,13 @@ int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) + } + + /* +- * ntfs_permission +- * +- * inode_operations::permission ++ * ntfs_permission - inode_operations::permission + */ + int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, + int mask) + { + if (ntfs_sb(inode->i_sb)->options.no_acs_rules) { +- /* "no access rules" mode - allow all changes */ ++ /* "No access rules" mode - Allow all changes. */ + return 0; + } + +@@ -783,9 +776,7 @@ int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, + } + + /* +- * ntfs_listxattr +- * +- * inode_operations::listxattr ++ * ntfs_listxattr - inode_operations::listxattr + */ + ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size) + { +@@ -815,7 +806,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, + struct ntfs_inode *ni = ntfs_i(inode); + size_t name_len = strlen(name); + +- /* Dispatch request */ ++ /* Dispatch request. */ + if (name_len == sizeof(SYSTEM_DOS_ATTRIB) - 1 && + !memcmp(name, SYSTEM_DOS_ATTRIB, sizeof(SYSTEM_DOS_ATTRIB))) { + /* system.dos_attrib */ +@@ -851,7 +842,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, + size_t sd_size = 0; + + if (!is_ntfs3(ni->mi.sbi)) { +- /* we should get nt4 security */ ++ /* We should get nt4 security. */ + err = -EINVAL; + goto out; + } else if (le32_to_cpu(ni->std_security_id) < +@@ -901,7 +892,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, + goto out; + } + #endif +- /* deal with ntfs extended attribute */ ++ /* Deal with NTFS extended attribute. */ + err = ntfs_get_ea(inode, name, name_len, buffer, size, NULL); + + out: +@@ -909,9 +900,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, + } + + /* +- * ntfs_setxattr +- * +- * inode_operations::setxattr ++ * ntfs_setxattr - inode_operations::setxattr + */ + static noinline int ntfs_setxattr(const struct xattr_handler *handler, + struct user_namespace *mnt_userns, +@@ -924,7 +913,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + size_t name_len = strlen(name); + enum FILE_ATTRIBUTE new_fa; + +- /* Dispatch request */ ++ /* Dispatch request. */ + if (name_len == sizeof(SYSTEM_DOS_ATTRIB) - 1 && + !memcmp(name, SYSTEM_DOS_ATTRIB, sizeof(SYSTEM_DOS_ATTRIB))) { + if (sizeof(u8) != size) +@@ -940,7 +929,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + new_fa = cpu_to_le32(*(u32 *)value); + + if (S_ISREG(inode->i_mode)) { +- /* Process compressed/sparsed in special way*/ ++ /* Process compressed/sparsed in special way. */ + ni_lock(ni); + err = ni_new_attr_flags(ni, new_fa); + ni_unlock(ni); +@@ -950,7 +939,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + set_new_fa: + /* + * Thanks Mark Harmstone: +- * keep directory bit consistency ++ * Keep directory bit consistency. + */ + if (S_ISDIR(inode->i_mode)) + new_fa |= FILE_ATTRIBUTE_DIRECTORY; +@@ -963,7 +952,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + inode->i_mode &= ~0222; + else + inode->i_mode |= 0222; +- /* std attribute always in primary record */ ++ /* Std attribute always in primary record. */ + ni->mi.dirty = true; + mark_inode_dirty(inode); + } +@@ -981,8 +970,8 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + + if (!is_ntfs3(ni->mi.sbi)) { + /* +- * we should replace ATTR_SECURE +- * Skip this way cause it is nt4 feature ++ * We should replace ATTR_SECURE. ++ * Skip this way cause it is nt4 feature. + */ + err = -EINVAL; + goto out; +@@ -1007,7 +996,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + err = -EINVAL; + } else if (std->security_id != security_id) { + std->security_id = ni->std_security_id = security_id; +- /* std attribute always in primary record */ ++ /* Std attribute always in primary record. */ + ni->mi.dirty = true; + mark_inode_dirty(&ni->vfs_inode); + } +@@ -1031,7 +1020,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + goto out; + } + #endif +- /* deal with ntfs extended attribute */ ++ /* Deal with NTFS extended attribute. */ + err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0); + + out: +-- +2.31.1 + diff --git a/SOURCES/0027-fs-ntfs3-Remove-fat-ioctl-s-from-ntfs3-driver-for-no.patch b/SOURCES/0027-fs-ntfs3-Remove-fat-ioctl-s-from-ntfs3-driver-for-no.patch new file mode 100644 index 0000000..553865d --- /dev/null +++ b/SOURCES/0027-fs-ntfs3-Remove-fat-ioctl-s-from-ntfs3-driver-for-no.patch @@ -0,0 +1,50 @@ +From a97131c29c997e81b6fa1d1adf8f3ca07b63a2e1 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 24 Aug 2021 21:20:20 +0300 +Subject: [Backport a97131c29c99] src: Remove fat ioctl's from ntfs3 + driver for now + +For some reason we have FAT ioctl calls. Even old ntfs driver did not +use these. We should not use these because it his hard to get things out +of kernel when they are upstream. That's why we remove these for now. + +More discussion is needed what ioctl should be implemented and what is +important. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/file.c | 8 -------- + 1 file changed, 8 deletions(-) + +diff --git a/src/file.c b/src/file.c +index 26346771d9dc765c6faddc96913bca512e1f768a..62ebfa324bff5814d0937ac13a043a408e4d3f4d 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -12,7 +12,6 @@ + #include + #include + #include +-#include /* FAT_IOCTL_XXX */ + #include + + #include "debug.h" +@@ -52,15 +51,8 @@ static long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg) + { + struct inode *inode = file_inode(filp); + struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; +- u32 __user *user_attr = (u32 __user *)arg; + + switch (cmd) { +- case FAT_IOCTL_GET_ATTRIBUTES: +- return put_user(le32_to_cpu(ntfs_i(inode)->std_fa), user_attr); +- +- case FAT_IOCTL_GET_VOLUME_ID: +- return put_user(sbi->volume.ser_num, user_attr); +- + case FITRIM: + return ntfs_ioctl_fitrim(sbi, arg); + } +-- +2.31.1 + diff --git a/SOURCES/0028-fs-ntfs3-Rework-file-operations.patch b/SOURCES/0028-fs-ntfs3-Rework-file-operations.patch new file mode 100644 index 0000000..08e7390 --- /dev/null +++ b/SOURCES/0028-fs-ntfs3-Rework-file-operations.patch @@ -0,0 +1,1978 @@ +From 78ab59fee07f22464f32eafebab2bd97ba94ff2d Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 31 Aug 2021 18:52:39 +0300 +Subject: [Backport 78ab59fee07f] src: Rework file operations + +Rename now works "Add new name and remove old name". +"Remove old name and add new name" may result in bad inode +if we can't add new name and then can't restore (add) old name. + +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 17 +-- + src/attrlist.c | 26 ++-- + src/frecord.c | 342 ++++++++++++++++++++++++++++++++++++-------- + src/fsntfs.c | 87 +++++------ + src/index.c | 45 +++--- + src/inode.c | 275 ++++++++++++----------------------- + src/namei.c | 236 ++++++++---------------------- + src/ntfs_fs.h | 31 +++- + src/record.c | 8 +- + src/xattr.c | 25 ++-- + 10 files changed, 563 insertions(+), 529 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index 4b285f704e621ef5cbba11f1a006622e6387ea09..ffc323bacc9fe63683395d9535ec65849137551a 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -218,9 +218,11 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, + } + + out: +- /* Undo. */ +- run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false); +- run_truncate(run, vcn0); ++ /* Undo 'ntfs_look_for_free_space' */ ++ if (vcn - vcn0) { ++ run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false); ++ run_truncate(run, vcn0); ++ } + + return err; + } +@@ -701,7 +703,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + * (list entry for std attribute always first). + * So it is safe to step back. + */ +- mi_remove_attr(mi, attr); ++ mi_remove_attr(NULL, mi, attr); + + if (!al_remove_le(ni, le)) { + err = -EINVAL; +@@ -1004,7 +1006,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, + end = next_svcn; + while (end > evcn) { + /* Remove segment [svcn : evcn). */ +- mi_remove_attr(mi, attr); ++ mi_remove_attr(NULL, mi, attr); + + if (!al_remove_le(ni, le)) { + err = -EINVAL; +@@ -1600,7 +1602,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, + end = next_svcn; + while (end > evcn) { + /* Remove segment [svcn : evcn). */ +- mi_remove_attr(mi, attr); ++ mi_remove_attr(NULL, mi, attr); + + if (!al_remove_le(ni, le)) { + err = -EINVAL; +@@ -1836,13 +1838,12 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) + u16 le_sz; + u16 roff = le16_to_cpu(attr->nres.run_off); + +- /* run==1 means unpack and deallocate. */ + run_unpack_ex(RUN_DEALLOCATE, sbi, ni->mi.rno, svcn, + evcn1 - 1, svcn, Add2Ptr(attr, roff), + le32_to_cpu(attr->size) - roff); + + /* Delete this attribute segment. */ +- mi_remove_attr(mi, attr); ++ mi_remove_attr(NULL, mi, attr); + if (!le) + break; + +diff --git a/src/attrlist.c b/src/attrlist.c +index 32ca990af64b53b4e9572d5dd0d6ae5f057760c4..fa32399eb5171ce25f037fbd1bd8e483a37b839c 100644 +--- a/src/attrlist.c ++++ b/src/attrlist.c +@@ -279,7 +279,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + struct ATTR_LIST_ENTRY *le; + size_t off; + u16 sz; +- size_t asize, new_asize; ++ size_t asize, new_asize, old_size; + u64 new_size; + typeof(ni->attr_list) *al = &ni->attr_list; + +@@ -287,8 +287,9 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + * Compute the size of the new 'le' + */ + sz = le_size(name_len); +- new_size = al->size + sz; +- asize = al_aligned(al->size); ++ old_size = al->size; ++ new_size = old_size + sz; ++ asize = al_aligned(old_size); + new_asize = al_aligned(new_size); + + /* Scan forward to the point at which the new 'le' should be inserted. */ +@@ -302,13 +303,14 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + return -ENOMEM; + + memcpy(ptr, al->le, off); +- memcpy(Add2Ptr(ptr, off + sz), le, al->size - off); ++ memcpy(Add2Ptr(ptr, off + sz), le, old_size - off); + le = Add2Ptr(ptr, off); + kfree(al->le); + al->le = ptr; + } else { +- memmove(Add2Ptr(le, sz), le, al->size - off); ++ memmove(Add2Ptr(le, sz), le, old_size - off); + } ++ *new_le = le; + + al->size = new_size; + +@@ -321,23 +323,25 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + le->id = id; + memcpy(le->name, name, sizeof(short) * name_len); + +- al->dirty = true; +- + err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size, + &new_size, true, &attr); +- if (err) ++ if (err) { ++ /* Undo memmove above. */ ++ memmove(le, Add2Ptr(le, sz), old_size - off); ++ al->size = old_size; + return err; ++ } ++ ++ al->dirty = true; + + if (attr && attr->non_res) { + err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, + al->size); + if (err) + return err; ++ al->dirty = false; + } + +- al->dirty = false; +- *new_le = le; +- + return 0; + } + +diff --git a/src/frecord.c b/src/frecord.c +index 9d374827750b2b39261f5985ec2fbb554ac8f6df..3f48b612ec962c6720fb3d3b3e0d63312f389c81 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -163,7 +163,7 @@ int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + /* + * ni_load_mi - Load mft_inode corresponded list_entry. + */ +-int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, ++int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le, + struct mft_inode **mi) + { + CLST rno; +@@ -402,7 +402,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (!attr) + return -ENOENT; + +- mi_remove_attr(&ni->mi, attr); ++ mi_remove_attr(ni, &ni->mi, attr); + return 0; + } + +@@ -441,7 +441,7 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (!attr) + return -ENOENT; + +- mi_remove_attr(mi, attr); ++ mi_remove_attr(ni, mi, attr); + + if (PtrOffset(ni->attr_list.le, le) >= ni->attr_list.size) + return 0; +@@ -454,12 +454,11 @@ int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + * + * Return: Not full constructed attribute or NULL if not possible to create. + */ +-static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, +- struct mft_inode *mi, +- struct ATTR_LIST_ENTRY *le, +- enum ATTR_TYPE type, const __le16 *name, +- u8 name_len, u32 asize, u16 name_off, +- CLST svcn) ++static struct ATTRIB * ++ni_ins_new_attr(struct ntfs_inode *ni, struct mft_inode *mi, ++ struct ATTR_LIST_ENTRY *le, enum ATTR_TYPE type, ++ const __le16 *name, u8 name_len, u32 asize, u16 name_off, ++ CLST svcn, struct ATTR_LIST_ENTRY **ins_le) + { + int err; + struct ATTRIB *attr; +@@ -507,6 +506,8 @@ static struct ATTRIB *ni_ins_new_attr(struct ntfs_inode *ni, + le->ref = ref; + + out: ++ if (ins_le) ++ *ins_le = le; + return attr; + } + +@@ -599,7 +600,7 @@ static int ni_repack(struct ntfs_inode *ni) + if (next_svcn >= evcn + 1) { + /* We can remove this attribute segment. */ + al_remove_le(ni, le); +- mi_remove_attr(mi, attr); ++ mi_remove_attr(NULL, mi, attr); + le = le_p; + continue; + } +@@ -695,8 +696,8 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + free -= asize; + } + +- /* Is seems that attribute list can be removed from primary record. */ +- mi_remove_attr(&ni->mi, attr_list); ++ /* It seems that attribute list can be removed from primary record. */ ++ mi_remove_attr(NULL, &ni->mi, attr_list); + + /* + * Repeat the cycle above and move all attributes to primary record. +@@ -724,7 +725,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + attr_ins->id = id; + + /* Remove from original record. */ +- mi_remove_attr(mi, attr); ++ mi_remove_attr(NULL, mi, attr); + } + + run_deallocate(sbi, &ni->attr_list.run, true); +@@ -842,7 +843,8 @@ int ni_create_attr_list(struct ntfs_inode *ni) + memcpy(attr, b, asize); + attr->id = le_b[nb]->id; + +- WARN_ON(!mi_remove_attr(&ni->mi, b)); ++ /* Remove from primary record. */ ++ WARN_ON(!mi_remove_attr(NULL, &ni->mi, b)); + + if (to_free <= asize) + break; +@@ -883,7 +885,8 @@ int ni_create_attr_list(struct ntfs_inode *ni) + static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + enum ATTR_TYPE type, const __le16 *name, u8 name_len, + u32 asize, CLST svcn, u16 name_off, bool force_ext, +- struct ATTRIB **ins_attr, struct mft_inode **ins_mi) ++ struct ATTRIB **ins_attr, struct mft_inode **ins_mi, ++ struct ATTR_LIST_ENTRY **ins_le) + { + struct ATTRIB *attr; + struct mft_inode *mi; +@@ -956,12 +959,14 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + + /* Try to insert attribute into this subrecord. */ + attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize, +- name_off, svcn); ++ name_off, svcn, ins_le); + if (!attr) + continue; + + if (ins_attr) + *ins_attr = attr; ++ if (ins_mi) ++ *ins_mi = mi; + return 0; + } + +@@ -977,7 +982,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + } + + attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize, +- name_off, svcn); ++ name_off, svcn, ins_le); + if (!attr) + goto out2; + +@@ -1016,7 +1021,8 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, u32 asize, + u16 name_off, CLST svcn, struct ATTRIB **ins_attr, +- struct mft_inode **ins_mi) ++ struct mft_inode **ins_mi, ++ struct ATTR_LIST_ENTRY **ins_le) + { + struct ntfs_sb_info *sbi = ni->mi.sbi; + int err; +@@ -1045,7 +1051,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + + if (asize <= free) { + attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len, +- asize, name_off, svcn); ++ asize, name_off, svcn, ins_le); + if (attr) { + if (ins_attr) + *ins_attr = attr; +@@ -1059,7 +1065,8 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + if (!is_mft || type != ATTR_DATA || svcn) { + /* This ATTRIB will be external. */ + err = ni_ins_attr_ext(ni, NULL, type, name, name_len, asize, +- svcn, name_off, false, ins_attr, ins_mi); ++ svcn, name_off, false, ins_attr, ins_mi, ++ ins_le); + goto out; + } + +@@ -1117,7 +1124,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + t16 = le16_to_cpu(attr->name_off); + err = ni_ins_attr_ext(ni, le, attr->type, Add2Ptr(attr, t16), + attr->name_len, t32, attr_svcn(attr), t16, +- false, &eattr, NULL); ++ false, &eattr, NULL, NULL); + if (err) + return err; + +@@ -1125,8 +1132,8 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + memcpy(eattr, attr, t32); + eattr->id = id; + +- /* Remove attrib from primary record. */ +- mi_remove_attr(&ni->mi, attr); ++ /* Remove from primary record. */ ++ mi_remove_attr(NULL, &ni->mi, attr); + + /* attr now points to next attribute. */ + if (attr->type == ATTR_END) +@@ -1136,7 +1143,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + ; + + attr = ni_ins_new_attr(ni, &ni->mi, NULL, type, name, name_len, asize, +- name_off, svcn); ++ name_off, svcn, ins_le); + if (!attr) { + err = -EINVAL; + goto out; +@@ -1251,7 +1258,7 @@ static int ni_expand_mft_list(struct ntfs_inode *ni) + */ + attr = ni_ins_new_attr(ni, mi_min, NULL, ATTR_DATA, NULL, 0, + SIZEOF_NONRESIDENT + run_size, +- SIZEOF_NONRESIDENT, svcn); ++ SIZEOF_NONRESIDENT, svcn, NULL); + if (!attr) { + err = -EINVAL; + goto out; +@@ -1315,14 +1322,15 @@ int ni_expand_list(struct ntfs_inode *ni) + err = ni_ins_attr_ext(ni, le, attr->type, attr_name(attr), + attr->name_len, asize, attr_svcn(attr), + le16_to_cpu(attr->name_off), true, +- &ins_attr, NULL); ++ &ins_attr, NULL, NULL); + + if (err) + goto out; + + memcpy(ins_attr, attr, asize); + ins_attr->id = le->id; +- mi_remove_attr(&ni->mi, attr); ++ /* Remove from primary record. */ ++ mi_remove_attr(NULL, &ni->mi, attr); + + done += asize; + goto out; +@@ -1382,7 +1390,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + err = ni_insert_attr(ni, type, name, name_len, asize, name_off, svcn, +- &attr, mi); ++ &attr, mi, NULL); + + if (err) + goto out; +@@ -1422,7 +1430,8 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + */ + int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + enum ATTR_TYPE type, const __le16 *name, u8 name_len, +- struct ATTRIB **new_attr, struct mft_inode **mi) ++ struct ATTRIB **new_attr, struct mft_inode **mi, ++ struct ATTR_LIST_ENTRY **le) + { + int err; + u32 name_size = ALIGN(name_len * sizeof(short), 8); +@@ -1430,7 +1439,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + struct ATTRIB *attr; + + err = ni_insert_attr(ni, type, name, name_len, asize, SIZEOF_RESIDENT, +- 0, &attr, mi); ++ 0, &attr, mi, le); + if (err) + return err; + +@@ -1439,8 +1448,13 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + + attr->res.data_size = cpu_to_le32(data_size); + attr->res.data_off = cpu_to_le16(SIZEOF_RESIDENT + name_size); +- if (type == ATTR_NAME) ++ if (type == ATTR_NAME) { + attr->res.flags = RESIDENT_FLAG_INDEXED; ++ ++ /* is_attr_indexed(attr)) == true */ ++ le16_add_cpu(&ni->mi.mrec->hard_links, +1); ++ ni->mi.dirty = true; ++ } + attr->res.res = 0; + + if (new_attr) +@@ -1452,22 +1466,13 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + /* + * ni_remove_attr_le - Remove attribute from record. + */ +-int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, +- struct ATTR_LIST_ENTRY *le) ++void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct mft_inode *mi, struct ATTR_LIST_ENTRY *le) + { +- int err; +- struct mft_inode *mi; +- +- err = ni_load_mi(ni, le, &mi); +- if (err) +- return err; +- +- mi_remove_attr(mi, attr); ++ mi_remove_attr(ni, mi, attr); + + if (le) + al_remove_le(ni, le); +- +- return 0; + } + + /* +@@ -1549,10 +1554,12 @@ int ni_delete_all(struct ntfs_inode *ni) + + /* ni_fname_name + * +- *Return: File name attribute by its value. */ ++ * Return: File name attribute by its value. ++ */ + struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + const struct cpu_str *uni, + const struct MFT_REF *home_dir, ++ struct mft_inode **mi, + struct ATTR_LIST_ENTRY **le) + { + struct ATTRIB *attr = NULL; +@@ -1562,7 +1569,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + + /* Enumerate all names. */ + next: +- attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, NULL); ++ attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, mi); + if (!attr) + return NULL; + +@@ -1592,6 +1599,7 @@ struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + * Return: File name attribute with given type. + */ + struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, ++ struct mft_inode **mi, + struct ATTR_LIST_ENTRY **le) + { + struct ATTRIB *attr = NULL; +@@ -1599,10 +1607,12 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, + + *le = NULL; + ++ if (FILE_NAME_POSIX == name_type) ++ return NULL; ++ + /* Enumerate all names. */ + for (;;) { +- attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, +- NULL); ++ attr = ni_find_attr(ni, attr, le, ATTR_NAME, NULL, 0, NULL, mi); + if (!attr) + return NULL; + +@@ -2788,6 +2798,222 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + return err; + } + ++/* ++ * ni_remove_name - Removes name 'de' from MFT and from directory. ++ * 'de2' and 'undo_step' are used to restore MFT/dir, if error occurs. ++ */ ++int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ++ struct NTFS_DE *de, struct NTFS_DE **de2, int *undo_step) ++{ ++ int err; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1); ++ struct ATTR_FILE_NAME *fname; ++ struct ATTR_LIST_ENTRY *le; ++ struct mft_inode *mi; ++ u16 de_key_size = le16_to_cpu(de->key_size); ++ u8 name_type; ++ ++ *undo_step = 0; ++ ++ /* Find name in record. */ ++ mi_get_ref(&dir_ni->mi, &de_name->home); ++ ++ fname = ni_fname_name(ni, (struct cpu_str *)&de_name->name_len, ++ &de_name->home, &mi, &le); ++ if (!fname) ++ return -ENOENT; ++ ++ memcpy(&de_name->dup, &fname->dup, sizeof(struct NTFS_DUP_INFO)); ++ name_type = paired_name(fname->type); ++ ++ /* Mark ntfs as dirty. It will be cleared at umount. */ ++ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); ++ ++ /* Step 1: Remove name from directory. */ ++ err = indx_delete_entry(&dir_ni->dir, dir_ni, fname, de_key_size, sbi); ++ if (err) ++ return err; ++ ++ /* Step 2: Remove name from MFT. */ ++ ni_remove_attr_le(ni, attr_from_name(fname), mi, le); ++ ++ *undo_step = 2; ++ ++ /* Get paired name. */ ++ fname = ni_fname_type(ni, name_type, &mi, &le); ++ if (fname) { ++ u16 de2_key_size = fname_full_size(fname); ++ ++ *de2 = Add2Ptr(de, 1024); ++ (*de2)->key_size = cpu_to_le16(de2_key_size); ++ ++ memcpy(*de2 + 1, fname, de2_key_size); ++ ++ /* Step 3: Remove paired name from directory. */ ++ err = indx_delete_entry(&dir_ni->dir, dir_ni, fname, ++ de2_key_size, sbi); ++ if (err) ++ return err; ++ ++ /* Step 4: Remove paired name from MFT. */ ++ ni_remove_attr_le(ni, attr_from_name(fname), mi, le); ++ ++ *undo_step = 4; ++ } ++ return 0; ++} ++ ++/* ++ * ni_remove_name_undo - Paired function for ni_remove_name. ++ * ++ * Return: True if ok ++ */ ++bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ++ struct NTFS_DE *de, struct NTFS_DE *de2, int undo_step) ++{ ++ struct ntfs_sb_info *sbi = ni->mi.sbi; ++ struct ATTRIB *attr; ++ u16 de_key_size = de2 ? le16_to_cpu(de2->key_size) : 0; ++ ++ switch (undo_step) { ++ case 4: ++ if (ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0, ++ &attr, NULL, NULL)) { ++ return false; ++ } ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de2 + 1, de_key_size); ++ ++ mi_get_ref(&ni->mi, &de2->ref); ++ de2->size = cpu_to_le16(ALIGN(de_key_size, 8) + ++ sizeof(struct NTFS_DE)); ++ de2->flags = 0; ++ de2->res = 0; ++ ++ if (indx_insert_entry(&dir_ni->dir, dir_ni, de2, sbi, NULL, ++ 1)) { ++ return false; ++ } ++ fallthrough; ++ ++ case 2: ++ de_key_size = le16_to_cpu(de->key_size); ++ ++ if (ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0, ++ &attr, NULL, NULL)) { ++ return false; ++ } ++ ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de + 1, de_key_size); ++ mi_get_ref(&ni->mi, &de->ref); ++ ++ if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1)) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * ni_add_name - Add new name in MFT and in directory. ++ */ ++int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ++ struct NTFS_DE *de) ++{ ++ int err; ++ struct ATTRIB *attr; ++ struct ATTR_LIST_ENTRY *le; ++ struct mft_inode *mi; ++ struct ATTR_FILE_NAME *de_name = (struct ATTR_FILE_NAME *)(de + 1); ++ u16 de_key_size = le16_to_cpu(de->key_size); ++ ++ mi_get_ref(&ni->mi, &de->ref); ++ mi_get_ref(&dir_ni->mi, &de_name->home); ++ ++ /* Insert new name in MFT. */ ++ err = ni_insert_resident(ni, de_key_size, ATTR_NAME, NULL, 0, &attr, ++ &mi, &le); ++ if (err) ++ return err; ++ ++ memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de_name, de_key_size); ++ ++ /* Insert new name in directory. */ ++ err = indx_insert_entry(&dir_ni->dir, dir_ni, de, ni->mi.sbi, NULL, 0); ++ if (err) ++ ni_remove_attr_le(ni, attr, mi, le); ++ ++ return err; ++} ++ ++/* ++ * ni_rename - Remove one name and insert new name. ++ */ ++int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, ++ struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de, ++ bool *is_bad) ++{ ++ int err; ++ struct NTFS_DE *de2 = NULL; ++ int undo = 0; ++ ++ /* ++ * There are two possible ways to rename: ++ * 1) Add new name and remove old name. ++ * 2) Remove old name and add new name. ++ * ++ * In most cases (not all!) adding new name in MFT and in directory can ++ * allocate additional cluster(s). ++ * Second way may result to bad inode if we can't add new name ++ * and then can't restore (add) old name. ++ */ ++ ++ /* ++ * Way 1 - Add new + remove old. ++ */ ++ err = ni_add_name(new_dir_ni, ni, new_de); ++ if (!err) { ++ err = ni_remove_name(dir_ni, ni, de, &de2, &undo); ++ if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo)) ++ *is_bad = true; ++ } ++ ++ /* ++ * Way 2 - Remove old + add new. ++ */ ++ /* ++ * err = ni_remove_name(dir_ni, ni, de, &de2, &undo); ++ * if (!err) { ++ * err = ni_add_name(new_dir_ni, ni, new_de); ++ * if (err && !ni_remove_name_undo(dir_ni, ni, de, de2, undo)) ++ * *is_bad = true; ++ * } ++ */ ++ ++ return err; ++} ++ ++/* ++ * ni_is_dirty - Return: True if 'ni' requires ni_write_inode. ++ */ ++bool ni_is_dirty(struct inode *inode) ++{ ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct rb_node *node; ++ ++ if (ni->mi.dirty || ni->attr_list.dirty || ++ (ni->ni_flags & NI_FLAG_UPDATE_PARENT)) ++ return true; ++ ++ for (node = rb_first(&ni->mi_tree); node; node = rb_next(node)) { ++ if (rb_entry(node, struct mft_inode, node)->dirty) ++ return true; ++ } ++ ++ return false; ++} ++ + /* + * ni_update_parent + * +@@ -2802,8 +3028,6 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + struct ntfs_sb_info *sbi = ni->mi.sbi; + struct super_block *sb = sbi->sb; + bool re_dirty = false; +- bool active = sb->s_flags & SB_ACTIVE; +- bool upd_parent = ni->ni_flags & NI_FLAG_UPDATE_PARENT; + + if (ni->mi.mrec->flags & RECORD_FLAG_DIR) { + dup->fa |= FILE_ATTRIBUTE_DIRECTORY; +@@ -2867,19 +3091,9 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + struct ATTR_FILE_NAME *fname; + + fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); +- if (!fname) ++ if (!fname || !memcmp(&fname->dup, dup, sizeof(fname->dup))) + continue; + +- if (memcmp(&fname->dup, dup, sizeof(fname->dup))) { +- memcpy(&fname->dup, dup, sizeof(fname->dup)); +- mi->dirty = true; +- } else if (!upd_parent) { +- continue; +- } +- +- if (!active) +- continue; /* Avoid __wait_on_freeing_inode(inode); */ +- + /* ntfs_iget5 may sleep. */ + dir = ntfs_iget5(sb, &fname->home, NULL); + if (IS_ERR(dir)) { +@@ -2898,6 +3112,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + } else { + indx_update_dup(dir_ni, sbi, fname, dup, sync); + ni_unlock(dir_ni); ++ memcpy(&fname->dup, dup, sizeof(fname->dup)); ++ mi->dirty = true; + } + } + iput(dir); +@@ -2969,7 +3185,9 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + ni->mi.dirty = true; + + if (!ntfs_is_meta_file(sbi, inode->i_ino) && +- (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT))) { ++ (modified || (ni->ni_flags & NI_FLAG_UPDATE_PARENT)) ++ /* Avoid __wait_on_freeing_inode(inode). */ ++ && (sb->s_flags & SB_ACTIVE)) { + dup.cr_time = std->cr_time; + /* Not critical if this function fail. */ + re_dirty = ni_update_parent(ni, &dup, sync); +@@ -3033,7 +3251,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + return err; + } + +- if (re_dirty && (sb->s_flags & SB_ACTIVE)) ++ if (re_dirty) + mark_inode_dirty_sync(inode); + + return 0; +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 0edb95ed9717f442f97b3ba3e85b4b6a361e0e27..66924943921771d3cf46f6a762b1ea81ae1e9ca2 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -358,29 +358,25 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + enum ALLOCATE_OPT opt) + { + int err; ++ CLST alen = 0; + struct super_block *sb = sbi->sb; +- size_t a_lcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen; ++ size_t alcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen; + struct wnd_bitmap *wnd = &sbi->used.bitmap; + + down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); + if (opt & ALLOCATE_MFT) { +- CLST alen; +- + zlen = wnd_zone_len(wnd); + + if (!zlen) { + err = ntfs_refresh_zone(sbi); + if (err) + goto out; +- + zlen = wnd_zone_len(wnd); ++ } + +- if (!zlen) { +- ntfs_err(sbi->sb, +- "no free space to extend mft"); +- err = -ENOSPC; +- goto out; +- } ++ if (!zlen) { ++ ntfs_err(sbi->sb, "no free space to extend mft"); ++ goto out; + } + + lcn = wnd_zone_bit(wnd); +@@ -389,14 +385,13 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + wnd_zone_set(wnd, lcn + alen, zlen - alen); + + err = wnd_set_used(wnd, lcn, alen); +- if (err) +- goto out; +- +- *new_lcn = lcn; +- *new_len = alen; +- goto ok; ++ if (err) { ++ up_write(&wnd->rw_lock); ++ return err; ++ } ++ alcn = lcn; ++ goto out; + } +- + /* + * 'Cause cluster 0 is always used this value means that we should use + * cached value of 'next_free_lcn' to improve performance. +@@ -407,22 +402,17 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + if (lcn >= wnd->nbits) + lcn = 0; + +- *new_len = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &a_lcn); +- if (*new_len) { +- *new_lcn = a_lcn; +- goto ok; +- } ++ alen = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &alcn); ++ if (alen) ++ goto out; + + /* Try to use clusters from MftZone. */ + zlen = wnd_zone_len(wnd); + zeroes = wnd_zeroes(wnd); + +- /* Check too big request. */ +- if (len > zeroes + zlen) +- goto no_space; +- +- if (zlen <= NTFS_MIN_MFT_ZONE) +- goto no_space; ++ /* Check too big request */ ++ if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE) ++ goto out; + + /* How many clusters to cat from zone. */ + zlcn = wnd_zone_bit(wnd); +@@ -439,31 +429,24 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + wnd_zone_set(wnd, zlcn, new_zlen); + + /* Allocate continues clusters. */ +- *new_len = +- wnd_find(wnd, len, 0, +- BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &a_lcn); +- if (*new_len) { +- *new_lcn = a_lcn; +- goto ok; +- } +- +-no_space: +- up_write(&wnd->rw_lock); +- +- return -ENOSPC; +- +-ok: +- err = 0; ++ alen = wnd_find(wnd, len, 0, ++ BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn); + +- ntfs_unmap_meta(sb, *new_lcn, *new_len); ++out: ++ if (alen) { ++ err = 0; ++ *new_len = alen; ++ *new_lcn = alcn; + +- if (opt & ALLOCATE_MFT) +- goto out; ++ ntfs_unmap_meta(sb, alcn, alen); + +- /* Set hint for next requests. */ +- sbi->used.next_free_lcn = *new_lcn + *new_len; ++ /* Set hint for next requests. */ ++ if (!(opt & ALLOCATE_MFT)) ++ sbi->used.next_free_lcn = alcn + alen; ++ } else { ++ err = -ENOSPC; ++ } + +-out: + up_write(&wnd->rw_lock); + return err; + } +@@ -2226,7 +2209,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + sii_e.sec_id = d_security->key.sec_id; + memcpy(&sii_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR); + +- err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL); ++ err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL, 0); + if (err) + goto out; + +@@ -2247,7 +2230,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + + fnd_clear(fnd_sdh); + err = indx_insert_entry(indx_sdh, ni, &sdh_e.de, (void *)(size_t)1, +- fnd_sdh); ++ fnd_sdh, 0); + if (err) + goto out; + +@@ -2385,7 +2368,7 @@ int ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag, + + mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE); + +- err = indx_insert_entry(indx, ni, &re.de, NULL, NULL); ++ err = indx_insert_entry(indx, ni, &re.de, NULL, NULL, 0); + + mark_inode_dirty(&ni->vfs_inode); + ni_unlock(ni); +diff --git a/src/index.c b/src/index.c +index 70ef59455b72c927b40bbb76c5420b8dfc72e24a..1224b8e42b3e3d07938310739843f0ab7195f326 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -1427,7 +1427,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + alloc->nres.valid_size = alloc->nres.data_size = cpu_to_le64(data_size); + + err = ni_insert_resident(ni, bitmap_size(1), ATTR_BITMAP, in->name, +- in->name_len, &bitmap, NULL); ++ in->name_len, &bitmap, NULL, NULL); + if (err) + goto out2; + +@@ -1443,7 +1443,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + return 0; + + out2: +- mi_remove_attr(&ni->mi, alloc); ++ mi_remove_attr(NULL, &ni->mi, alloc); + + out1: + run_deallocate(sbi, &run, false); +@@ -1529,24 +1529,24 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + /* + * indx_insert_into_root - Attempt to insert an entry into the index root. + * ++ * @undo - True if we undoing previous remove. + * If necessary, it will twiddle the index b-tree. + */ + static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + const struct NTFS_DE *new_de, + struct NTFS_DE *root_de, const void *ctx, +- struct ntfs_fnd *fnd) ++ struct ntfs_fnd *fnd, bool undo) + { + int err = 0; + struct NTFS_DE *e, *e0, *re; + struct mft_inode *mi; + struct ATTRIB *attr; +- struct MFT_REC *rec; + struct INDEX_HDR *hdr; + struct indx_node *n; + CLST new_vbn; + __le64 *sub_vbn, t_vbn; + u16 new_de_size; +- u32 hdr_used, hdr_total, asize, used, to_move; ++ u32 hdr_used, hdr_total, asize, to_move; + u32 root_size, new_root_size; + struct ntfs_sb_info *sbi; + int ds_root; +@@ -1559,12 +1559,11 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + + /* + * Try easy case: +- * hdr_insert_de will succeed if there's room the root for the new entry. ++ * hdr_insert_de will succeed if there's ++ * room the root for the new entry. + */ + hdr = &root->ihdr; + sbi = ni->mi.sbi; +- rec = mi->mrec; +- used = le32_to_cpu(rec->used); + new_de_size = le16_to_cpu(new_de->size); + hdr_used = le32_to_cpu(hdr->used); + hdr_total = le32_to_cpu(hdr->total); +@@ -1573,9 +1572,9 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + + ds_root = new_de_size + hdr_used - hdr_total; + +- if (used + ds_root < sbi->max_bytes_per_attr) { +- /* Make a room for new elements. */ +- mi_resize_attr(mi, attr, ds_root); ++ /* If 'undo' is set then reduce requirements. */ ++ if ((undo || asize + ds_root < sbi->max_bytes_per_attr) && ++ mi_resize_attr(mi, attr, ds_root)) { + hdr->total = cpu_to_le32(hdr_total + ds_root); + e = hdr_insert_de(indx, hdr, new_de, root_de, ctx); + WARN_ON(!e); +@@ -1629,7 +1628,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + sizeof(u64); + ds_root = new_root_size - root_size; + +- if (ds_root > 0 && used + ds_root > sbi->max_bytes_per_attr) { ++ if (ds_root > 0 && asize + ds_root > sbi->max_bytes_per_attr) { + /* Make root external. */ + err = -EOPNOTSUPP; + goto out_free_re; +@@ -1710,7 +1709,7 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, + + put_indx_node(n); + fnd_clear(fnd); +- err = indx_insert_entry(indx, ni, new_de, ctx, fnd); ++ err = indx_insert_entry(indx, ni, new_de, ctx, fnd, undo); + goto out_free_root; + } + +@@ -1854,7 +1853,7 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + */ + if (!level) { + /* Insert in root. */ +- err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd); ++ err = indx_insert_into_root(indx, ni, up_e, NULL, ctx, fnd, 0); + if (err) + goto out; + } else { +@@ -1876,10 +1875,12 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, + + /* + * indx_insert_entry - Insert new entry into index. ++ * ++ * @undo - True if we undoing previous remove. + */ + int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + const struct NTFS_DE *new_de, const void *ctx, +- struct ntfs_fnd *fnd) ++ struct ntfs_fnd *fnd, bool undo) + { + int err; + int diff; +@@ -1925,7 +1926,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + * new entry into it. + */ + err = indx_insert_into_root(indx, ni, new_de, fnd->root_de, ctx, +- fnd); ++ fnd, undo); + if (err) + goto out; + } else { +@@ -2302,7 +2303,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + fnd->level - 1, + fnd) + : indx_insert_into_root(indx, ni, re, e, +- ctx, fnd); ++ ctx, fnd, 0); + kfree(re); + + if (err) +@@ -2507,7 +2508,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + * Re-insert the entry into the tree. + * Find the spot the tree where we want to insert the new entry. + */ +- err = indx_insert_entry(indx, ni, me, ctx, fnd); ++ err = indx_insert_entry(indx, ni, me, ctx, fnd, 0); + kfree(me); + if (err) + goto out; +@@ -2595,10 +2596,8 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, + struct ntfs_index *indx = &ni->dir; + + fnd = fnd_get(); +- if (!fnd) { +- err = -ENOMEM; +- goto out1; +- } ++ if (!fnd) ++ return -ENOMEM; + + root = indx_get_root(indx, ni, NULL, &mi); + if (!root) { +@@ -2645,7 +2644,5 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, + + out: + fnd_put(fnd); +- +-out1: + return err; + } +diff --git a/src/inode.c b/src/inode.c +index b86ec7dd731cea9fd0b41abb40fe4d81ca965c13..8f72066b3229a3f87b7b0b0e770bce20a4b06edd 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -399,6 +399,12 @@ static struct inode *ntfs_read_mft(struct inode *inode, + goto out; + } + ++ if (names != le16_to_cpu(rec->hard_links)) { ++ /* Correct minor error on the fly. Do not mark inode as dirty. */ ++ rec->hard_links = cpu_to_le16(names); ++ ni->mi.dirty = true; ++ } ++ + set_nlink(inode, names); + + if (S_ISDIR(mode)) { +@@ -1279,6 +1285,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + } + inode = &ni->vfs_inode; + inode_init_owner(mnt_userns, inode, dir, mode); ++ mode = inode->i_mode; + + inode->i_atime = inode->i_mtime = inode->i_ctime = ni->i_crtime = + current_time(inode); +@@ -1371,6 +1378,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + attr = Add2Ptr(attr, asize); + } + ++ attr->id = cpu_to_le16(aid++); + if (fa & FILE_ATTRIBUTE_DIRECTORY) { + /* + * Regular directory or symlink to directory. +@@ -1381,7 +1389,6 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + attr->type = ATTR_ROOT; + attr->size = cpu_to_le32(asize); +- attr->id = cpu_to_le16(aid++); + + attr->name_len = ARRAY_SIZE(I30_NAME); + attr->name_off = SIZEOF_RESIDENT_LE; +@@ -1412,52 +1419,46 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + /* Insert empty ATTR_DATA */ + attr->type = ATTR_DATA; + attr->size = cpu_to_le32(SIZEOF_RESIDENT); +- attr->id = cpu_to_le16(aid++); + attr->name_off = SIZEOF_RESIDENT_LE; + attr->res.data_off = SIZEOF_RESIDENT_LE; +- } else { ++ } else if (S_ISREG(mode)) { + /* +- * Regular file or node. ++ * Regular file. Create empty non resident data attribute. + */ + attr->type = ATTR_DATA; +- attr->id = cpu_to_le16(aid++); +- +- if (S_ISREG(mode)) { +- /* Create empty non resident data attribute. */ +- attr->non_res = 1; +- attr->nres.evcn = cpu_to_le64(-1ll); +- if (fa & FILE_ATTRIBUTE_SPARSE_FILE) { +- attr->size = +- cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); +- attr->name_off = SIZEOF_NONRESIDENT_EX_LE; +- attr->flags = ATTR_FLAG_SPARSED; +- asize = SIZEOF_NONRESIDENT_EX + 8; +- } else if (fa & FILE_ATTRIBUTE_COMPRESSED) { +- attr->size = +- cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); +- attr->name_off = SIZEOF_NONRESIDENT_EX_LE; +- attr->flags = ATTR_FLAG_COMPRESSED; +- attr->nres.c_unit = COMPRESSION_UNIT; +- asize = SIZEOF_NONRESIDENT_EX + 8; +- } else { +- attr->size = +- cpu_to_le32(SIZEOF_NONRESIDENT + 8); +- attr->name_off = SIZEOF_NONRESIDENT_LE; +- asize = SIZEOF_NONRESIDENT + 8; +- } +- attr->nres.run_off = attr->name_off; ++ attr->non_res = 1; ++ attr->nres.evcn = cpu_to_le64(-1ll); ++ if (fa & FILE_ATTRIBUTE_SPARSE_FILE) { ++ attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ attr->flags = ATTR_FLAG_SPARSED; ++ asize = SIZEOF_NONRESIDENT_EX + 8; ++ } else if (fa & FILE_ATTRIBUTE_COMPRESSED) { ++ attr->size = cpu_to_le32(SIZEOF_NONRESIDENT_EX + 8); ++ attr->name_off = SIZEOF_NONRESIDENT_EX_LE; ++ attr->flags = ATTR_FLAG_COMPRESSED; ++ attr->nres.c_unit = COMPRESSION_UNIT; ++ asize = SIZEOF_NONRESIDENT_EX + 8; + } else { +- /* Create empty resident data attribute. */ +- attr->size = cpu_to_le32(SIZEOF_RESIDENT); +- attr->name_off = SIZEOF_RESIDENT_LE; +- if (fa & FILE_ATTRIBUTE_SPARSE_FILE) +- attr->flags = ATTR_FLAG_SPARSED; +- else if (fa & FILE_ATTRIBUTE_COMPRESSED) +- attr->flags = ATTR_FLAG_COMPRESSED; +- attr->res.data_off = SIZEOF_RESIDENT_LE; +- asize = SIZEOF_RESIDENT; +- ni->ni_flags |= NI_FLAG_RESIDENT; ++ attr->size = cpu_to_le32(SIZEOF_NONRESIDENT + 8); ++ attr->name_off = SIZEOF_NONRESIDENT_LE; ++ asize = SIZEOF_NONRESIDENT + 8; + } ++ attr->nres.run_off = attr->name_off; ++ } else { ++ /* ++ * Node. Create empty resident data attribute. ++ */ ++ attr->type = ATTR_DATA; ++ attr->size = cpu_to_le32(SIZEOF_RESIDENT); ++ attr->name_off = SIZEOF_RESIDENT_LE; ++ if (fa & FILE_ATTRIBUTE_SPARSE_FILE) ++ attr->flags = ATTR_FLAG_SPARSED; ++ else if (fa & FILE_ATTRIBUTE_COMPRESSED) ++ attr->flags = ATTR_FLAG_COMPRESSED; ++ attr->res.data_off = SIZEOF_RESIDENT_LE; ++ asize = SIZEOF_RESIDENT; ++ ni->ni_flags |= NI_FLAG_RESIDENT; + } + + if (S_ISDIR(mode)) { +@@ -1485,7 +1486,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + asize = ALIGN(SIZEOF_RESIDENT + nsize, 8); + t16 = PtrOffset(rec, attr); + +- if (asize + t16 + 8 > sbi->record_size) { ++ /* 0x78 - the size of EA + EAINFO to store WSL */ ++ if (asize + t16 + 0x78 + 8 > sbi->record_size) { + CLST alen; + CLST clst = bytes_to_cluster(sbi, nsize); + +@@ -1545,20 +1547,15 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + rec->next_attr_id = cpu_to_le16(aid); + + /* Step 2: Add new name in index. */ +- err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd); ++ err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, fnd, 0); + if (err) + goto out6; + +- /* Update current directory record. */ +- mark_inode_dirty(dir); +- + inode->i_generation = le16_to_cpu(rec->seq); + + dir->i_mtime = dir->i_ctime = inode->i_atime; + + if (S_ISDIR(mode)) { +- if (dir->i_mode & S_ISGID) +- mode |= S_ISGID; + inode->i_op = &ntfs_dir_inode_operations; + inode->i_fop = &ntfs_dir_operations; + } else if (S_ISLNK(mode)) { +@@ -1601,8 +1598,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + d_instantiate(dentry, inode); + + ntfs_save_wsl_perm(inode); +- mark_inode_dirty(inode); + mark_inode_dirty(dir); ++ mark_inode_dirty(inode); + + /* Normal exit. */ + goto out2; +@@ -1646,61 +1643,36 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + int ntfs_link_inode(struct inode *inode, struct dentry *dentry) + { + int err; +- struct inode *dir = d_inode(dentry->d_parent); +- struct ntfs_inode *dir_ni = ntfs_i(dir); + struct ntfs_inode *ni = ntfs_i(inode); +- struct super_block *sb = inode->i_sb; +- struct ntfs_sb_info *sbi = sb->s_fs_info; +- const struct qstr *name = &dentry->d_name; +- struct NTFS_DE *new_de = NULL; +- struct ATTR_FILE_NAME *fname; +- struct ATTRIB *attr; +- u16 key_size; +- struct INDEX_ROOT *dir_root; +- +- dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL); +- if (!dir_root) +- return -EINVAL; ++ struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; ++ struct NTFS_DE *de; ++ struct ATTR_FILE_NAME *de_name; + + /* Allocate PATH_MAX bytes. */ +- new_de = __getname(); +- if (!new_de) ++ de = __getname(); ++ if (!de) + return -ENOMEM; + +- /* Mark rw ntfs as dirty. It will be cleared at umount. */ +- ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY); +- +- /* Insert file name. */ +- err = fill_name_de(sbi, new_de, name, NULL); +- if (err) +- goto out; +- +- key_size = le16_to_cpu(new_de->key_size); +- err = ni_insert_resident(ni, key_size, ATTR_NAME, NULL, 0, &attr, NULL); +- if (err) +- goto out; +- +- mi_get_ref(&ni->mi, &new_de->ref); +- +- fname = (struct ATTR_FILE_NAME *)(new_de + 1); +- mi_get_ref(&dir_ni->mi, &fname->home); +- fname->dup.cr_time = fname->dup.m_time = fname->dup.c_time = +- fname->dup.a_time = kernel2nt(&inode->i_ctime); +- fname->dup.alloc_size = fname->dup.data_size = 0; +- fname->dup.fa = ni->std_fa; +- fname->dup.ea_size = fname->dup.reparse = 0; +- +- memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), fname, key_size); ++ /* Mark rw ntfs as dirty. It will be cleared at umount. */ ++ ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); + +- err = indx_insert_entry(&dir_ni->dir, dir_ni, new_de, sbi, NULL); ++ /* Construct 'de'. */ ++ err = fill_name_de(sbi, de, &dentry->d_name, NULL); + if (err) + goto out; + +- le16_add_cpu(&ni->mi.mrec->hard_links, 1); +- ni->mi.dirty = true; ++ de_name = (struct ATTR_FILE_NAME *)(de + 1); ++ /* Fill duplicate info. */ ++ de_name->dup.cr_time = de_name->dup.m_time = de_name->dup.c_time = ++ de_name->dup.a_time = kernel2nt(&inode->i_ctime); ++ de_name->dup.alloc_size = de_name->dup.data_size = ++ cpu_to_le64(inode->i_size); ++ de_name->dup.fa = ni->std_fa; ++ de_name->dup.ea_size = de_name->dup.reparse = 0; + ++ err = ni_add_name(ntfs_i(d_inode(dentry->d_parent)), ni, de); + out: +- __putname(new_de); ++ __putname(de); + return err; + } + +@@ -1713,113 +1685,56 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) + int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) + { + int err; +- struct super_block *sb = dir->i_sb; +- struct ntfs_sb_info *sbi = sb->s_fs_info; ++ struct ntfs_sb_info *sbi = dir->i_sb->s_fs_info; + struct inode *inode = d_inode(dentry); + struct ntfs_inode *ni = ntfs_i(inode); +- const struct qstr *name = &dentry->d_name; + struct ntfs_inode *dir_ni = ntfs_i(dir); +- struct ntfs_index *indx = &dir_ni->dir; +- struct cpu_str *uni = NULL; +- struct ATTR_FILE_NAME *fname; +- u8 name_type; +- struct ATTR_LIST_ENTRY *le; +- struct MFT_REF ref; +- bool is_dir = S_ISDIR(inode->i_mode); +- struct INDEX_ROOT *dir_root; ++ struct NTFS_DE *de, *de2 = NULL; ++ int undo_remove; + +- dir_root = indx_get_root(indx, dir_ni, NULL, NULL); +- if (!dir_root) ++ if (ntfs_is_meta_file(sbi, ni->mi.rno)) + return -EINVAL; + ++ /* Allocate PATH_MAX bytes. */ ++ de = __getname(); ++ if (!de) ++ return -ENOMEM; ++ + ni_lock(ni); + +- if (is_dir && !dir_is_empty(inode)) { ++ if (S_ISDIR(inode->i_mode) && !dir_is_empty(inode)) { + err = -ENOTEMPTY; +- goto out1; +- } +- +- if (ntfs_is_meta_file(sbi, inode->i_ino)) { +- err = -EINVAL; +- goto out1; +- } +- +- /* Allocate PATH_MAX bytes. */ +- uni = __getname(); +- if (!uni) { +- err = -ENOMEM; +- goto out1; ++ goto out; + } + +- /* Convert input string to unicode. */ +- err = ntfs_nls_to_utf16(sbi, name->name, name->len, uni, NTFS_NAME_LEN, +- UTF16_HOST_ENDIAN); ++ err = fill_name_de(sbi, de, &dentry->d_name, NULL); + if (err < 0) +- goto out2; +- +- /* Mark rw ntfs as dirty. It will be cleared at umount. */ +- ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); +- +- /* Find name in record. */ +- mi_get_ref(&dir_ni->mi, &ref); +- +- le = NULL; +- fname = ni_fname_name(ni, uni, &ref, &le); +- if (!fname) { +- err = -ENOENT; +- goto out3; +- } +- +- name_type = paired_name(fname->type); +- +- err = indx_delete_entry(indx, dir_ni, fname, fname_full_size(fname), +- sbi); +- if (err) +- goto out3; +- +- /* Then remove name from MFT. */ +- ni_remove_attr_le(ni, attr_from_name(fname), le); +- +- le16_add_cpu(&ni->mi.mrec->hard_links, -1); +- ni->mi.dirty = true; +- +- if (name_type != FILE_NAME_POSIX) { +- /* Now we should delete name by type. */ +- fname = ni_fname_type(ni, name_type, &le); +- if (fname) { +- err = indx_delete_entry(indx, dir_ni, fname, +- fname_full_size(fname), sbi); +- if (err) +- goto out3; ++ goto out; + +- ni_remove_attr_le(ni, attr_from_name(fname), le); ++ undo_remove = 0; ++ err = ni_remove_name(dir_ni, ni, de, &de2, &undo_remove); + +- le16_add_cpu(&ni->mi.mrec->hard_links, -1); +- } +- } +-out3: +- switch (err) { +- case 0: ++ if (!err) { + drop_nlink(inode); +- break; +- case -ENOTEMPTY: +- case -ENOSPC: +- case -EROFS: +- break; +- default: ++ dir->i_mtime = dir->i_ctime = current_time(dir); ++ mark_inode_dirty(dir); ++ inode->i_ctime = dir->i_ctime; ++ if (inode->i_nlink) ++ mark_inode_dirty(inode); ++ } else if (!ni_remove_name_undo(dir_ni, ni, de, de2, undo_remove)) { + make_bad_inode(inode); ++ ntfs_inode_err(inode, "failed to undo unlink"); ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ } else { ++ if (ni_is_dirty(dir)) ++ mark_inode_dirty(dir); ++ if (ni_is_dirty(inode)) ++ mark_inode_dirty(inode); + } + +- dir->i_mtime = dir->i_ctime = current_time(dir); +- mark_inode_dirty(dir); +- inode->i_ctime = dir->i_ctime; +- if (inode->i_nlink) +- mark_inode_dirty(inode); +- +-out2: +- __putname(uni); +-out1: ++out: + ni_unlock(ni); ++ __putname(de); + return err; + } + +diff --git a/src/namei.c b/src/namei.c +index f79a399bd015056f320714c91b935afac16eb488..e58415d0713280297df1b7bac30eb17642431fa0 100644 +--- a/src/namei.c ++++ b/src/namei.c +@@ -152,12 +152,14 @@ static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de) + if (inode != dir) + ni_lock(ni); + +- dir->i_ctime = dir->i_mtime = inode->i_ctime = current_time(inode); + inc_nlink(inode); + ihold(inode); + + err = ntfs_link_inode(inode, de); ++ + if (!err) { ++ dir->i_ctime = dir->i_mtime = inode->i_ctime = ++ current_time(dir); + mark_inode_dirty(inode); + mark_inode_dirty(dir); + d_instantiate(de, inode); +@@ -249,25 +251,26 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) + /* + * ntfs_rename - inode_operations::rename + */ +-static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, +- struct dentry *old_dentry, struct inode *new_dir, ++static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *dir, ++ struct dentry *dentry, struct inode *new_dir, + struct dentry *new_dentry, u32 flags) + { + int err; +- struct super_block *sb = old_dir->i_sb; ++ struct super_block *sb = dir->i_sb; + struct ntfs_sb_info *sbi = sb->s_fs_info; +- struct ntfs_inode *old_dir_ni = ntfs_i(old_dir); ++ struct ntfs_inode *dir_ni = ntfs_i(dir); + struct ntfs_inode *new_dir_ni = ntfs_i(new_dir); +- struct ntfs_inode *old_ni; +- struct ATTR_FILE_NAME *old_name, *new_name, *fname; +- u8 name_type; +- bool is_same; +- struct inode *old_inode, *new_inode; +- struct NTFS_DE *old_de, *new_de; +- struct ATTRIB *attr; +- struct ATTR_LIST_ENTRY *le; +- u16 new_de_key_size; +- ++ struct inode *inode = d_inode(dentry); ++ struct ntfs_inode *ni = ntfs_i(inode); ++ struct inode *new_inode = d_inode(new_dentry); ++ struct NTFS_DE *de, *new_de; ++ bool is_same, is_bad; ++ /* ++ * de - memory of PATH_MAX bytes: ++ * [0-1024) - original name (dentry->d_name) ++ * [1024-2048) - paired to original name, usually DOS variant of dentry->d_name ++ * [2048-3072) - new name (new_dentry->d_name) ++ */ + static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + SIZEOF_RESIDENT < 1024); + static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + sizeof(struct NTFS_DE) < + 1024); +@@ -276,24 +279,18 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + +- old_inode = d_inode(old_dentry); +- new_inode = d_inode(new_dentry); +- +- old_ni = ntfs_i(old_inode); ++ is_same = dentry->d_name.len == new_dentry->d_name.len && ++ !memcmp(dentry->d_name.name, new_dentry->d_name.name, ++ dentry->d_name.len); + +- is_same = old_dentry->d_name.len == new_dentry->d_name.len && +- !memcmp(old_dentry->d_name.name, new_dentry->d_name.name, +- old_dentry->d_name.len); +- +- if (is_same && old_dir == new_dir) { ++ if (is_same && dir == new_dir) { + /* Nothing to do. */ +- err = 0; +- goto out; ++ return 0; + } + +- if (ntfs_is_meta_file(sbi, old_inode->i_ino)) { +- err = -EINVAL; +- goto out; ++ if (ntfs_is_meta_file(sbi, inode->i_ino)) { ++ /* Should we print an error? */ ++ return -EINVAL; + } + + if (new_inode) { +@@ -304,168 +301,61 @@ static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir, + ni_unlock(new_dir_ni); + dput(new_dentry); + if (err) +- goto out; ++ return err; + } + + /* Allocate PATH_MAX bytes. */ +- old_de = __getname(); +- if (!old_de) { +- err = -ENOMEM; +- goto out; +- } ++ de = __getname(); ++ if (!de) ++ return -ENOMEM; + +- err = fill_name_de(sbi, old_de, &old_dentry->d_name, NULL); ++ /* Translate dentry->d_name into unicode form. */ ++ err = fill_name_de(sbi, de, &dentry->d_name, NULL); + if (err < 0) +- goto out1; +- +- old_name = (struct ATTR_FILE_NAME *)(old_de + 1); ++ goto out; + + if (is_same) { +- new_de = old_de; ++ /* Reuse 'de'. */ ++ new_de = de; + } else { +- new_de = Add2Ptr(old_de, 1024); ++ /* Translate new_dentry->d_name into unicode form. */ ++ new_de = Add2Ptr(de, 2048); + err = fill_name_de(sbi, new_de, &new_dentry->d_name, NULL); + if (err < 0) +- goto out1; +- } +- +- ni_lock_dir(old_dir_ni); +- ni_lock(old_ni); +- +- mi_get_ref(&old_dir_ni->mi, &old_name->home); +- +- /* Get pointer to file_name in MFT. */ +- fname = ni_fname_name(old_ni, (struct cpu_str *)&old_name->name_len, +- &old_name->home, &le); +- if (!fname) { +- err = -EINVAL; +- goto out2; ++ goto out; + } + +- /* Copy fname info from record into new fname. */ +- new_name = (struct ATTR_FILE_NAME *)(new_de + 1); +- memcpy(&new_name->dup, &fname->dup, sizeof(fname->dup)); +- +- name_type = paired_name(fname->type); +- +- /* Remove first name from directory. */ +- err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1, +- le16_to_cpu(old_de->key_size), sbi); +- if (err) +- goto out3; +- +- /* Remove first name from MFT. */ +- err = ni_remove_attr_le(old_ni, attr_from_name(fname), le); +- if (err) +- goto out4; +- +- le16_add_cpu(&old_ni->mi.mrec->hard_links, -1); +- old_ni->mi.dirty = true; +- +- if (name_type != FILE_NAME_POSIX) { +- /* Get paired name. */ +- fname = ni_fname_type(old_ni, name_type, &le); +- if (fname) { +- /* Remove second name from directory. */ +- err = indx_delete_entry(&old_dir_ni->dir, old_dir_ni, +- fname, fname_full_size(fname), +- sbi); +- if (err) +- goto out5; +- +- /* Remove second name from MFT. */ +- err = ni_remove_attr_le(old_ni, attr_from_name(fname), +- le); +- if (err) +- goto out6; +- +- le16_add_cpu(&old_ni->mi.mrec->hard_links, -1); +- old_ni->mi.dirty = true; ++ ni_lock_dir(dir_ni); ++ ni_lock(ni); ++ ++ is_bad = false; ++ err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de, &is_bad); ++ if (is_bad) { ++ /* Restore after failed rename failed too. */ ++ make_bad_inode(inode); ++ ntfs_inode_err(inode, "failed to undo rename"); ++ ntfs_set_state(sbi, NTFS_DIRTY_ERROR); ++ } else if (!err) { ++ inode->i_ctime = dir->i_ctime = dir->i_mtime = ++ current_time(dir); ++ mark_inode_dirty(inode); ++ mark_inode_dirty(dir); ++ if (dir != new_dir) { ++ new_dir->i_mtime = new_dir->i_ctime = dir->i_ctime; ++ mark_inode_dirty(new_dir); + } +- } +- +- /* Add new name. */ +- mi_get_ref(&old_ni->mi, &new_de->ref); +- mi_get_ref(&ntfs_i(new_dir)->mi, &new_name->home); +- +- new_de_key_size = le16_to_cpu(new_de->key_size); +- +- /* Insert new name in MFT. */ +- err = ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0, +- &attr, NULL); +- if (err) +- goto out7; +- +- attr->res.flags = RESIDENT_FLAG_INDEXED; +- +- memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), new_name, new_de_key_size); +- +- le16_add_cpu(&old_ni->mi.mrec->hard_links, 1); +- old_ni->mi.dirty = true; +- +- /* Insert new name in directory. */ +- err = indx_insert_entry(&new_dir_ni->dir, new_dir_ni, new_de, sbi, +- NULL); +- if (err) +- goto out8; + +- if (IS_DIRSYNC(new_dir)) +- err = ntfs_sync_inode(old_inode); +- else +- mark_inode_dirty(old_inode); ++ if (IS_DIRSYNC(dir)) ++ ntfs_sync_inode(dir); + +- old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); +- if (IS_DIRSYNC(old_dir)) +- (void)ntfs_sync_inode(old_dir); +- else +- mark_inode_dirty(old_dir); +- +- if (old_dir != new_dir) { +- new_dir->i_mtime = new_dir->i_ctime = old_dir->i_ctime; +- mark_inode_dirty(new_dir); +- } +- +- if (old_inode) { +- old_inode->i_ctime = old_dir->i_ctime; +- mark_inode_dirty(old_inode); ++ if (IS_DIRSYNC(new_dir)) ++ ntfs_sync_inode(inode); + } + +- err = 0; +- /* Normal way* */ +- goto out2; +- +-out8: +- /* undo +- * ni_insert_resident(old_ni, new_de_key_size, ATTR_NAME, NULL, 0, +- * &attr, NULL); +- */ +- mi_remove_attr(&old_ni->mi, attr); +-out7: +- /* undo +- * ni_remove_attr_le(old_ni, attr_from_name(fname), le); +- */ +-out6: +- /* undo +- * indx_delete_entry(&old_dir_ni->dir, old_dir_ni, +- * fname, fname_full_size(fname), +- * sbi); +- */ +-out5: +- /* undo +- * ni_remove_attr_le(old_ni, attr_from_name(fname), le); +- */ +-out4: +- /* undo: +- * indx_delete_entry(&old_dir_ni->dir, old_dir_ni, old_de + 1, +- * old_de->key_size, NULL); +- */ +-out3: +-out2: +- ni_unlock(old_ni); +- ni_unlock(old_dir_ni); +-out1: +- __putname(old_de); ++ ni_unlock(ni); ++ ni_unlock(dir_ni); + out: ++ __putname(de); + return err; + } + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 64ef92e16363c4973be6438efdeea0663ce10e84..f9436cbbc347b617b458bb21d6c7229b467ca289 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -478,7 +478,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni); + struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni); + void ni_clear(struct ntfs_inode *ni); + int ni_load_mi_ex(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi); +-int ni_load_mi(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, ++int ni_load_mi(struct ntfs_inode *ni, const struct ATTR_LIST_ENTRY *le, + struct mft_inode **mi); + struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr, + struct ATTR_LIST_ENTRY **entry_o, +@@ -505,15 +505,18 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, + struct mft_inode **mi); + int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + enum ATTR_TYPE type, const __le16 *name, u8 name_len, +- struct ATTRIB **new_attr, struct mft_inode **mi); +-int ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, +- struct ATTR_LIST_ENTRY *le); ++ struct ATTRIB **new_attr, struct mft_inode **mi, ++ struct ATTR_LIST_ENTRY **le); ++void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr, ++ struct mft_inode *mi, struct ATTR_LIST_ENTRY *le); + int ni_delete_all(struct ntfs_inode *ni); + struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni, + const struct cpu_str *uni, + const struct MFT_REF *home, ++ struct mft_inode **mi, + struct ATTR_LIST_ENTRY **entry); + struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, ++ struct mft_inode **mi, + struct ATTR_LIST_ENTRY **entry); + int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa); + enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, +@@ -528,6 +531,21 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + u32 pages_per_frame); + int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + u32 pages_per_frame); ++int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ++ struct NTFS_DE *de, struct NTFS_DE **de2, int *undo_step); ++ ++bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ++ struct NTFS_DE *de, struct NTFS_DE *de2, ++ int undo_step); ++ ++int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, ++ struct NTFS_DE *de); ++ ++int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni, ++ struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de, ++ bool *is_bad); ++ ++bool ni_is_dirty(struct inode *inode); + + /* Globals from fslog.c */ + int log_replay(struct ntfs_inode *ni, bool *initialized); +@@ -631,7 +649,7 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + size_t *off, struct ntfs_fnd *fnd); + int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + const struct NTFS_DE *new_de, const void *param, +- struct ntfs_fnd *fnd); ++ struct ntfs_fnd *fnd, bool undo); + int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, + const void *key, u32 key_len, const void *param); + int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, +@@ -694,7 +712,8 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, u32 asize, + u16 name_off); + +-bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr); ++bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi, ++ struct ATTRIB *attr); + bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes); + int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr, + struct runs_tree *run, CLST len); +diff --git a/src/record.c b/src/record.c +index d48a5e6c5045c5f154f3c1aba4108f458e9676b0..61e3f2fb619f698ffdf5d81ca6f488008002de52 100644 +--- a/src/record.c ++++ b/src/record.c +@@ -489,7 +489,8 @@ struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type, + * + * NOTE: The source attr will point to next attribute. + */ +-bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr) ++bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi, ++ struct ATTRIB *attr) + { + struct MFT_REC *rec = mi->mrec; + u32 aoff = PtrOffset(rec, attr); +@@ -499,6 +500,11 @@ bool mi_remove_attr(struct mft_inode *mi, struct ATTRIB *attr) + if (aoff + asize > used) + return false; + ++ if (ni && is_attr_indexed(attr)) { ++ le16_add_cpu(&ni->mi.mrec->hard_links, -1); ++ ni->mi.dirty = true; ++ } ++ + used -= asize; + memmove(attr, Add2Ptr(attr, asize), used - aoff); + rec->used = cpu_to_le32(used); +diff --git a/src/xattr.c b/src/xattr.c +index b4c921e4bc1a1b4bbb1c51ef7cef6677967b8e13..22fd5eb32c5be7f53b3fd8076599f7dbaf84e2cf 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -395,11 +395,13 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + } + + err = ni_insert_resident(ni, sizeof(struct EA_INFO), +- ATTR_EA_INFO, NULL, 0, NULL, NULL); ++ ATTR_EA_INFO, NULL, 0, NULL, NULL, ++ NULL); + if (err) + goto out; + +- err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL); ++ err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL, ++ NULL); + if (err) + goto out; + } +@@ -419,9 +421,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + + if (!size) { + /* Delete xattr, ATTR_EA_INFO */ +- err = ni_remove_attr_le(ni, attr, le); +- if (err) +- goto out; ++ ni_remove_attr_le(ni, attr, mi, le); + } else { + p = resident_data_ex(attr, sizeof(struct EA_INFO)); + if (!p) { +@@ -441,9 +441,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + + if (!size) { + /* Delete xattr, ATTR_EA */ +- err = ni_remove_attr_le(ni, attr, le); +- if (err) +- goto out; ++ ni_remove_attr_le(ni, attr, mi, le); + } else if (attr->non_res) { + err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size); + if (err) +@@ -605,8 +603,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + goto out; + } + +- err = ntfs_set_ea(inode, name, name_len, value, size, +- acl ? 0 : XATTR_REPLACE, locked); ++ err = ntfs_set_ea(inode, name, name_len, value, size, 0, locked); + if (!err) + set_cached_acl(inode, type, acl); + +@@ -632,8 +629,10 @@ static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns, + struct posix_acl *acl; + int err; + +- if (!(inode->i_sb->s_flags & SB_POSIXACL)) ++ if (!(inode->i_sb->s_flags & SB_POSIXACL)) { ++ ntfs_inode_warn(inode, "add mount option \"acl\" to use acl"); + return -EOPNOTSUPP; ++ } + + acl = ntfs_get_acl(inode, type); + if (IS_ERR(acl)) +@@ -655,8 +654,10 @@ static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns, + struct posix_acl *acl; + int err; + +- if (!(inode->i_sb->s_flags & SB_POSIXACL)) ++ if (!(inode->i_sb->s_flags & SB_POSIXACL)) { ++ ntfs_inode_warn(inode, "add mount option \"acl\" to use acl"); + return -EOPNOTSUPP; ++ } + + if (!inode_owner_or_capable(mnt_userns, inode)) + return -EPERM; +-- +2.31.1 + diff --git a/SOURCES/0029-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch b/SOURCES/0029-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch new file mode 100644 index 0000000..8f71c19 --- /dev/null +++ b/SOURCES/0029-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch @@ -0,0 +1,608 @@ +From d3624466b56dd5b1886c1dff500525b544c19c83 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 31 Aug 2021 16:57:40 +0300 +Subject: [Backport d3624466b56d] src: Restyle comments to better align + with kernel-doc + +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 18 +++++++++++------- + src/bitmap.c | 5 ++--- + src/file.c | 18 +++++++++--------- + src/frecord.c | 27 +++++++++++++-------------- + src/fslog.c | 11 +++++++---- + src/fsntfs.c | 8 ++++---- + src/index.c | 8 +++++--- + src/inode.c | 20 ++++++++++---------- + src/lznt.c | 5 +++-- + src/ntfs.h | 2 +- + src/ntfs_fs.h | 24 ++++++++++++------------ + src/record.c | 2 +- + src/super.c | 2 +- + src/upcase.c | 2 +- + src/xattr.c | 7 ++++--- + 15 files changed, 84 insertions(+), 75 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index ffc323bacc9fe63683395d9535ec65849137551a..34c4cbf7e29bc49bdb3fa060fd2c235c3fb68536 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -199,6 +199,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run, + + /* Add new fragment into run storage. */ + if (!run_add_entry(run, vcn, lcn, flen, opt == ALLOCATE_MFT)) { ++ /* Undo last 'ntfs_look_for_free_space' */ + down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); + wnd_set_free(wnd, lcn, flen); + up_write(&wnd->rw_lock); +@@ -351,7 +352,6 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + run_close(run); + out1: + kfree(attr_s); +- /* Reinsert le. */ + out: + return err; + } +@@ -1153,14 +1153,18 @@ int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, + u16 ro; + + attr = ni_find_attr(ni, NULL, NULL, type, name, name_len, &vcn, NULL); +- if (!attr) ++ if (!attr) { ++ /* Is record corrupted? */ + return -ENOENT; ++ } + + svcn = le64_to_cpu(attr->nres.svcn); + evcn = le64_to_cpu(attr->nres.evcn); + +- if (evcn < vcn || vcn < svcn) ++ if (evcn < vcn || vcn < svcn) { ++ /* Is record corrupted? */ + return -EINVAL; ++ } + + ro = le16_to_cpu(attr->nres.run_off); + err = run_unpack_ex(run, ni->mi.sbi, ni->mi.rno, svcn, evcn, svcn, +@@ -1171,7 +1175,7 @@ int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type, + } + + /* +- * attr_wof_load_runs_range - Load runs for given range [from to). ++ * attr_load_runs_range - Load runs for given range [from to). + */ + int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, u8 name_len, struct runs_tree *run, +@@ -1974,7 +1978,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + total_size = le64_to_cpu(attr_b->nres.total_size); + + if (vbo >= alloc_size) { +- // NOTE: It is allowed. ++ /* NOTE: It is allowed. */ + return 0; + } + +@@ -1986,9 +1990,9 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + bytes -= vbo; + + if ((vbo & mask) || (bytes & mask)) { +- /* We have to zero a range(s)*/ ++ /* We have to zero a range(s). */ + if (frame_size == NULL) { +- /* Caller insists range is aligned */ ++ /* Caller insists range is aligned. */ + return -EINVAL; + } + *frame_size = mask + 1; +diff --git a/src/bitmap.c b/src/bitmap.c +index 06ae38adb8ad5601ca134cad3b76871df5d7d3e8..831501555009533d8b3eac04b6258e7072bd6304 100644 +--- a/src/bitmap.c ++++ b/src/bitmap.c +@@ -29,7 +29,6 @@ struct rb_node_key { + size_t key; + }; + +-/* Tree is sorted by start (key). */ + struct e_node { + struct rb_node_key start; /* Tree sorted by start. */ + struct rb_node_key count; /* Tree sorted by len. */ +@@ -1117,7 +1116,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + sb = wnd->sb; + log2_bits = sb->s_blocksize_bits + 3; + +- /* At most two ranges [hint, max_alloc) + [0, hint) */ ++ /* At most two ranges [hint, max_alloc) + [0, hint). */ + Again: + + /* TODO: Optimize request for case nbits > wbits. */ +@@ -1241,7 +1240,7 @@ size_t wnd_find(struct wnd_bitmap *wnd, size_t to_alloc, size_t hint, + continue; + } + +- /* Read window */ ++ /* Read window. */ + bh = wnd_map(wnd, iw); + if (IS_ERR(bh)) { + // TODO: Error. +diff --git a/src/file.c b/src/file.c +index 62ebfa324bff5814d0937ac13a043a408e4d3f4d..89557d60a9b0f0e657ce9465504b417bbb50ed04 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -190,7 +190,8 @@ static int ntfs_extend_initialized_size(struct file *file, + + /* + * ntfs_zero_range - Helper function for punch_hole. +- * It zeroes a range [vbo, vbo_to) ++ * ++ * It zeroes a range [vbo, vbo_to). + */ + static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) + { +@@ -231,12 +232,12 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) + + if (!buffer_mapped(bh)) { + ntfs_get_block(inode, iblock, bh, 0); +- /* unmapped? It's a hole - nothing to do */ ++ /* Unmapped? It's a hole - nothing to do. */ + if (!buffer_mapped(bh)) + continue; + } + +- /* Ok, it's mapped. Make sure it's up-to-date */ ++ /* Ok, it's mapped. Make sure it's up-to-date. */ + if (PageUptodate(page)) + set_buffer_uptodate(bh); + +@@ -272,9 +273,8 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to) + } + + /* +- * ntfs_sparse_cluster ++ * ntfs_sparse_cluster - Helper function to zero a new allocated clusters. + * +- * Helper function to zero a new allocated clusters + * NOTE: 512 <= cluster size <= 2M + */ + void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn, +@@ -588,7 +588,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + truncate_pagecache(inode, vbo_down); + + if (!is_sparsed(ni) && !is_compressed(ni)) { +- /* normal file */ ++ /* Normal file. */ + err = ntfs_zero_range(inode, vbo, end); + goto out; + } +@@ -599,7 +599,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + if (err != E_NTFS_NOTALIGNED) + goto out; + +- /* process not aligned punch */ ++ /* Process not aligned punch. */ + mask = frame_size - 1; + vbo_a = (vbo + mask) & ~mask; + end_a = end & ~mask; +@@ -647,7 +647,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + if (err) + goto out; + +- /* Wait for existing dio to complete */ ++ /* Wait for existing dio to complete. */ + inode_dio_wait(inode); + + truncate_pagecache(inode, vbo_down); +@@ -1127,7 +1127,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) + goto out; + + if (WARN_ON(ni->ni_flags & NI_FLAG_COMPRESSED_MASK)) { +- /* Should never be here, see ntfs_file_open() */ ++ /* Should never be here, see ntfs_file_open(). */ + ret = -EOPNOTSUPP; + goto out; + } +diff --git a/src/frecord.c b/src/frecord.c +index 3f48b612ec962c6720fb3d3b3e0d63312f389c81..938b12d56ca676308d2af82666a4f6f19cc8f122 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -56,7 +56,7 @@ static struct mft_inode *ni_find_mi(struct ntfs_inode *ni, CLST rno) + + /* + * ni_add_mi - Add new mft_inode into ntfs_inode. +-*/ ++ */ + static void ni_add_mi(struct ntfs_inode *ni, struct mft_inode *mi) + { + ni_ins_mi(ni, &ni->mi_tree, mi->rno, &mi->node); +@@ -70,9 +70,8 @@ void ni_remove_mi(struct ntfs_inode *ni, struct mft_inode *mi) + rb_erase(&mi->node, &ni->mi_tree); + } + +-/* ni_std +- * +- * Return: Pointer into std_info from primary record. ++/* ++ * ni_std - Return: Pointer into std_info from primary record. + */ + struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni) + { +@@ -385,7 +384,7 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi) + + /* + * ni_remove_attr - Remove all attributes for the given type/name/id. +-*/ ++ */ + int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + const __le16 *name, size_t name_len, bool base_only, + const __le16 *id) +@@ -740,7 +739,7 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + + /* + * ni_create_attr_list - Generates an attribute list for this primary record. +-*/ ++ */ + int ni_create_attr_list(struct ntfs_inode *ni) + { + struct ntfs_sb_info *sbi = ni->mi.sbi; +@@ -939,7 +938,7 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + if (is_mft_data && + (mi_enum_attr(mi, NULL) || + vbo <= ((u64)mi->rno << sbi->record_bits))) { +- /* We can't accept this record 'case MFT's bootstrapping. */ ++ /* We can't accept this record 'cause MFT's bootstrapping. */ + continue; + } + if (is_mft && +@@ -1078,7 +1077,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + */ + max_free = free; + +- /* Estimate the result of moving all possible attributes away.*/ ++ /* Estimate the result of moving all possible attributes away. */ + attr = NULL; + + while ((attr = mi_enum_attr(&ni->mi, attr))) { +@@ -1095,7 +1094,7 @@ static int ni_insert_attr(struct ntfs_inode *ni, enum ATTR_TYPE type, + goto out; + } + +- /* Start real attribute moving */ ++ /* Start real attribute moving. */ + attr = NULL; + + for (;;) { +@@ -1542,7 +1541,7 @@ int ni_delete_all(struct ntfs_inode *ni) + node = next; + } + +- /* Free base record */ ++ /* Free base record. */ + clear_rec_inuse(ni->mi.mrec); + ni->mi.dirty = true; + err = mi_write(&ni->mi, 0); +@@ -2243,7 +2242,7 @@ int ni_decompress_file(struct ntfs_inode *ni) + } + + if (attr->non_res && is_attr_sparsed(attr)) { +- /* Sarsed attribute header is 8 bytes bigger than normal. */ ++ /* Sparsed attribute header is 8 bytes bigger than normal. */ + struct MFT_REC *rec = mi->mrec; + u32 used = le32_to_cpu(rec->used); + u32 asize = le32_to_cpu(attr->size); +@@ -2324,7 +2323,7 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + mutex_lock(&sbi->compress.mtx_xpress); + ctx = sbi->compress.xpress; + if (!ctx) { +- /* Lazy initialize Xpress decompress context */ ++ /* Lazy initialize Xpress decompress context. */ + ctx = xpress_allocate_decompressor(); + if (!ctx) { + err = -ENOMEM; +@@ -2348,7 +2347,7 @@ static int decompress_lzx_xpress(struct ntfs_sb_info *sbi, const char *cmpr, + /* + * ni_read_frame + * +- * Pages - array of locked pages. ++ * Pages - Array of locked pages. + */ + int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages, + u32 pages_per_frame) +@@ -2740,7 +2739,7 @@ int ni_write_frame(struct ntfs_inode *ni, struct page **pages, + lznt = NULL; + } + +- /* Compress: frame_mem -> frame_ondisk. */ ++ /* Compress: frame_mem -> frame_ondisk */ + compr_size = compress_lznt(frame_mem, frame_size, frame_ondisk, + frame_size, sbi->compress.lznt); + mutex_unlock(&sbi->compress.mtx_lznt); +diff --git a/src/fslog.c b/src/fslog.c +index 6f6057129fddd602ce0f86cd2aafba6b5c9fa356..b5853aed0e25bc6d93b3f6f667ae36feda3d980b 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -1362,7 +1362,8 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + /* Compute the log page values. */ + log->data_off = ALIGN( + offsetof(struct RECORD_PAGE_HDR, fixups) + +- sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1), 8); ++ sizeof(short) * ((log->page_size >> SECTOR_SHIFT) + 1), ++ 8); + log->data_size = log->page_size - log->data_off; + log->record_header_len = sizeof(struct LFS_RECORD_HDR); + +@@ -1372,7 +1373,9 @@ static void log_create(struct ntfs_log *log, u32 l_size, const u64 last_lsn, + /* Compute the restart page values. */ + log->ra_off = ALIGN( + offsetof(struct RESTART_HDR, fixups) + +- sizeof(short) * ((log->sys_page_size >> SECTOR_SHIFT) + 1), 8); ++ sizeof(short) * ++ ((log->sys_page_size >> SECTOR_SHIFT) + 1), ++ 8); + log->restart_size = log->sys_page_size - log->ra_off; + log->ra_size = struct_size(log->ra, clients, 1); + log->current_openlog_count = open_log_count; +@@ -5132,8 +5135,8 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + rh->sys_page_size = cpu_to_le32(log->page_size); + rh->page_size = cpu_to_le32(log->page_size); + +- t16 = ALIGN(offsetof(struct RESTART_HDR, fixups) + +- sizeof(short) * t16, 8); ++ t16 = ALIGN(offsetof(struct RESTART_HDR, fixups) + sizeof(short) * t16, ++ 8); + rh->ra_off = cpu_to_le16(t16); + rh->minor_ver = cpu_to_le16(1); // 0x1A: + rh->major_ver = cpu_to_le16(1); // 0x1C: +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 66924943921771d3cf46f6a762b1ea81ae1e9ca2..91e3743e1442f10cfc727eba0587a9ef0b20c039 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -312,7 +312,7 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) + if (sb_rdonly(sb) || !initialized) + goto out; + +- /* Fill LogFile by '-1' if it is initialized.ssss */ ++ /* Fill LogFile by '-1' if it is initialized. */ + err = ntfs_bio_fill_1(sbi, &ni->file.run); + + out: +@@ -960,10 +960,10 @@ int ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) + /* verify(!ntfs_update_mftmirr()); */ + + /* +- * if we used wait=1, sync_inode_metadata waits for the io for the ++ * If we used wait=1, sync_inode_metadata waits for the io for the + * inode to finish. It hangs when media is removed. + * So wait=0 is sent down to sync_inode_metadata +- * and filemap_fdatawrite is used for the data blocks ++ * and filemap_fdatawrite is used for the data blocks. + */ + err = sync_inode_metadata(&ni->vfs_inode, 0); + if (!err) +@@ -1917,7 +1917,7 @@ int ntfs_security_init(struct ntfs_sb_info *sbi) + sbi->security.next_id = SECURITY_ID_FIRST; + /* Always write new security at the end of bucket. */ + sbi->security.next_off = +- ALIGN(sds_size - SecurityDescriptorsBlockSize, 16); ++ ALIGN(sds_size - SecurityDescriptorsBlockSize, 16); + + off = 0; + ne = NULL; +diff --git a/src/index.c b/src/index.c +index 1224b8e42b3e3d07938310739843f0ab7195f326..0daca9adc54c79084743efc2487b49a0ac0d3227 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -2624,17 +2624,19 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi, + e_fname = (struct ATTR_FILE_NAME *)(e + 1); + + if (!memcmp(&e_fname->dup, dup, sizeof(*dup))) { +- /* Nothing to update in index! Try to avoid this call. */ ++ /* ++ * Nothing to update in index! Try to avoid this call. ++ */ + goto out; + } + + memcpy(&e_fname->dup, dup, sizeof(*dup)); + + if (fnd->level) { +- /* directory entry in index */ ++ /* Directory entry in index. */ + err = indx_write(indx, ni, fnd->nodes[fnd->level - 1], sync); + } else { +- /* directory entry in directory MFT record */ ++ /* Directory entry in directory MFT record. */ + mi->dirty = true; + if (sync) + err = mi_write(mi, 1); +diff --git a/src/inode.c b/src/inode.c +index 8f72066b3229a3f87b7b0b0e770bce20a4b06edd..db2a5a4c38e4d62abfb2c7ca16947cd766982b13 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -89,7 +89,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + } + + if (le32_to_cpu(rec->total) != sbi->record_size) { +- // Bad inode? ++ /* Bad inode? */ + err = -EINVAL; + goto out; + } +@@ -605,7 +605,7 @@ static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo, + if (vbo >= valid) + set_buffer_new(bh); + } else if (create) { +- /*normal write*/ ++ /* Normal write. */ + if (bytes > bh->b_size) + bytes = bh->b_size; + +@@ -1091,7 +1091,7 @@ int inode_write_data(struct inode *inode, const void *data, size_t bytes) + /* + * ntfs_reparse_bytes + * +- * Number of bytes to for REPARSE_DATA_BUFFER(IO_REPARSE_TAG_SYMLINK) ++ * Number of bytes for REPARSE_DATA_BUFFER(IO_REPARSE_TAG_SYMLINK) + * for unicode string of @uni_len length. + */ + static inline u32 ntfs_reparse_bytes(u32 uni_len) +@@ -1205,13 +1205,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + return ERR_PTR(-EINVAL); + + if (S_ISDIR(mode)) { +- /* use parent's directory attributes */ ++ /* Use parent's directory attributes. */ + fa = dir_ni->std_fa | FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_ARCHIVE; + /* +- * By default child directory inherits parent attributes +- * root directory is hidden + system +- * Make an exception for children in root ++ * By default child directory inherits parent attributes. ++ * Root directory is hidden + system. ++ * Make an exception for children in root. + */ + if (dir->i_ino == MFT_REC_ROOT) + fa &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); +@@ -1220,8 +1220,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + fa = FILE_ATTRIBUTE_REPARSE_POINT; + + /* +- * linux: there are dir/file/symlink and so on. +- * NTFS: symlinks are "dir + reparse" or "file + reparse". ++ * Linux: there are dir/file/symlink and so on. ++ * NTFS: symlinks are "dir + reparse" or "file + reparse" + * It is good idea to create: + * dir + reparse if 'symname' points to directory + * or +@@ -1860,7 +1860,7 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + + default: + if (IsReparseTagMicrosoft(rp->ReparseTag)) { +- /* unknown Microsoft Tag */ ++ /* Unknown Microsoft Tag. */ + goto out; + } + if (!IsReparseTagNameSurrogate(rp->ReparseTag) || +diff --git a/src/lznt.c b/src/lznt.c +index 3acf0d9f0b1511d967a6613e05c5845046b97549..f1f691a67cc490254957f062cfc38ec12ac7e834 100644 +--- a/src/lznt.c ++++ b/src/lznt.c +@@ -296,8 +296,9 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, + */ + struct lznt *get_lznt_ctx(int level) + { +- struct lznt *r = kzalloc(level ? offsetof(struct lznt, hash) : +- sizeof(struct lznt), GFP_NOFS); ++ struct lznt *r = kzalloc(level ? offsetof(struct lznt, hash) ++ : sizeof(struct lznt), ++ GFP_NOFS); + + if (r) + r->std = !level; +diff --git a/src/ntfs.h b/src/ntfs.h +index 0fd7bffb98d4dfe030ee2b4eaf9fa75647f8369d..6bb3e595263b67c7cc66a0fecc5dfa68310826d4 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -262,7 +262,7 @@ enum RECORD_FLAG { + RECORD_FLAG_UNKNOWN = cpu_to_le16(0x0008), + }; + +-/* MFT Record structure, */ ++/* MFT Record structure. */ + struct MFT_REC { + struct NTFS_RECORD_HEADER rhdr; // 'FILE' + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index f9436cbbc347b617b458bb21d6c7229b467ca289..97e682ebcfb94dd1ad9677ed5ee82bb6a88401a5 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -59,18 +59,18 @@ struct ntfs_mount_options { + u16 fs_fmask_inv; + u16 fs_dmask_inv; + +- unsigned uid : 1, /* uid was set. */ +- gid : 1, /* gid was set. */ +- fmask : 1, /* fmask was set. */ +- dmask : 1, /* dmask was set. */ +- sys_immutable : 1,/* Immutable system files. */ +- discard : 1, /* Issue discard requests on deletions. */ +- sparse : 1, /* Create sparse files. */ +- showmeta : 1, /* Show meta files. */ +- nohidden : 1, /* Do not show hidden files. */ +- force : 1, /* Rw mount dirty volume. */ +- no_acs_rules : 1,/*Exclude acs rules. */ +- prealloc : 1 /* Preallocate space when file is growing. */ ++ unsigned uid : 1, /* uid was set. */ ++ gid : 1, /* gid was set. */ ++ fmask : 1, /* fmask was set. */ ++ dmask : 1, /* dmask was set. */ ++ sys_immutable : 1, /* Immutable system files. */ ++ discard : 1, /* Issue discard requests on deletions. */ ++ sparse : 1, /* Create sparse files. */ ++ showmeta : 1, /* Show meta files. */ ++ nohidden : 1, /* Do not show hidden files. */ ++ force : 1, /* Rw mount dirty volume. */ ++ no_acs_rules : 1, /*Exclude acs rules. */ ++ prealloc : 1 /* Preallocate space when file is growing. */ + ; + }; + +diff --git a/src/record.c b/src/record.c +index 61e3f2fb619f698ffdf5d81ca6f488008002de52..103705c86772f45a90227e6434449e99c4d7d94b 100644 +--- a/src/record.c ++++ b/src/record.c +@@ -219,7 +219,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr) + + asize = le32_to_cpu(attr->size); + if (asize < SIZEOF_RESIDENT) { +- /* Impossible 'cause we should not return such attribute */ ++ /* Impossible 'cause we should not return such attribute. */ + return NULL; + } + +diff --git a/src/super.c b/src/super.c +index 2fbab8a931eeb7a3c69471fb1fcb1c3e8b0d582a..dbecf095da59268da2fede022b71cb2b40b2d909 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1053,7 +1053,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + iput(inode); + +- /* Load $LogFile to replay. */ ++ /* Load LogFile to replay. */ + ref.low = cpu_to_le32(MFT_REC_LOG); + ref.seq = cpu_to_le16(MFT_REC_LOG); + inode = ntfs_iget5(sb, &ref, &NAME_LOGFILE); +diff --git a/src/upcase.c b/src/upcase.c +index eb65bbd939e8cdf17d209b60243e5fddeddabdf3..bbeba778237eefc318b00e04b05ad0ac1592e1ec 100644 +--- a/src/upcase.c ++++ b/src/upcase.c +@@ -34,7 +34,7 @@ static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr) + * - Case insensitive + * - If name equals and 'bothcases' then + * - Case sensitive +- * 'Straigth way' code scans input names twice in worst case. ++ * 'Straight way' code scans input names twice in worst case. + * Optimized code scans input names only once. + */ + int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2, +diff --git a/src/xattr.c b/src/xattr.c +index 22fd5eb32c5be7f53b3fd8076599f7dbaf84e2cf..b15d532e4a17087b06284d82965c21224f1d4f35 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -26,9 +26,10 @@ + static inline size_t unpacked_ea_size(const struct EA_FULL *ea) + { + return ea->size ? le32_to_cpu(ea->size) +- : ALIGN(struct_size( +- ea, name, +- 1 + ea->name_len + le16_to_cpu(ea->elength)), 4); ++ : ALIGN(struct_size(ea, name, ++ 1 + ea->name_len + ++ le16_to_cpu(ea->elength)), ++ 4); + } + + static inline size_t packed_ea_size(const struct EA_FULL *ea) +-- +2.31.1 + diff --git a/SOURCES/0030-fs-ntfs3-Fix-integer-overflow-in-ni_fiemap-with-fiem.patch b/SOURCES/0030-fs-ntfs3-Fix-integer-overflow-in-ni_fiemap-with-fiem.patch new file mode 100644 index 0000000..732511e --- /dev/null +++ b/SOURCES/0030-fs-ntfs3-Fix-integer-overflow-in-ni_fiemap-with-fiem.patch @@ -0,0 +1,44 @@ +From d4e8e135a9af7d8d939bba1874ab314322fc2dc2 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Wed, 25 Aug 2021 21:24:35 +0300 +Subject: [Backport d4e8e135a9af] src: Fix integer overflow in ni_fiemap + with fiemap_prep() + +Use fiemap_prep() to check valid flags. It also shrink request scope +(@len) to what the fs can actually handle. + +This address following Smatch static checker warning: + src/frecord.c:1894 ni_fiemap() + warn: potential integer overflow from user 'vbo + len' + +Because fiemap_prep() shrinks @len this cannot happened anymore. + +Reported-by: Dan Carpenter +Link: lore.kernel.org/ntfs3/20210825080440.GA17407@kili/ + +Fixes: 4342306f0f0d ("src: Add file operations and implementation") +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/file.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/file.c b/src/file.c +index 89557d60a9b0f0e657ce9465504b417bbb50ed04..f9c9a8c91b46d6fad597c954c3b4a2cd331ebfb6 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -1212,8 +1212,9 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + int err; + struct ntfs_inode *ni = ntfs_i(inode); + +- if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) +- return -EOPNOTSUPP; ++ err = fiemap_prep(inode, fieinfo, start, &len, ~FIEMAP_FLAG_XATTR); ++ if (err) ++ return err; + + ni_lock(ni); + +-- +2.31.1 + diff --git a/SOURCES/0031-fs-ntfs3-Remove-unnecessary-condition-checking-from-.patch b/SOURCES/0031-fs-ntfs3-Remove-unnecessary-condition-checking-from-.patch new file mode 100644 index 0000000..3efb3bc --- /dev/null +++ b/SOURCES/0031-fs-ntfs3-Remove-unnecessary-condition-checking-from-.patch @@ -0,0 +1,46 @@ +From dd854e4b5b12016d27bfa2694226a1c15acbd640 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Wed, 25 Aug 2021 21:25:22 +0300 +Subject: [Backport dd854e4b5b12] src: Remove unnecessary condition + checking from ntfs_file_read_iter + +This check will be also performed in generic_file_read_iter() so we do +not want to check this two times in a row. + +This was founded with Smatch + src/file.c:803 ntfs_file_read_iter() + warn: unused return: count = iov_iter_count() + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/file.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/src/file.c b/src/file.c +index f9c9a8c91b46d6fad597c954c3b4a2cd331ebfb6..424450e77ad52ae550e8de444dc393411d97cde6 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -793,8 +793,6 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + + static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) + { +- ssize_t err; +- size_t count = iov_iter_count(iter); + struct file *file = iocb->ki_filp; + struct inode *inode = file->f_mapping->host; + struct ntfs_inode *ni = ntfs_i(inode); +@@ -823,9 +821,7 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) + return -EOPNOTSUPP; + } + +- err = count ? generic_file_read_iter(iocb, iter) : 0; +- +- return err; ++ return generic_file_read_iter(iocb, iter); + } + + /* +-- +2.31.1 + diff --git a/SOURCES/0032-fs-ntfs3-Remove-GPL-boilerplates-from-decompress-lib.patch b/SOURCES/0032-fs-ntfs3-Remove-GPL-boilerplates-from-decompress-lib.patch new file mode 100644 index 0000000..1694449 --- /dev/null +++ b/SOURCES/0032-fs-ntfs3-Remove-GPL-boilerplates-from-decompress-lib.patch @@ -0,0 +1,121 @@ +From 989e795bfe360cca0affb0a4224f37bd8494b46d Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Fri, 27 Aug 2021 00:44:41 +0300 +Subject: [Backport 989e795bfe36] src: Remove GPL boilerplates from + decompress lib files + +Files already have SDPX identifier so no reason to keep boilerplates in +these files anymore. + +Signed-off-by: Kari Argillander +Acked-by: Eric Biggers +Signed-off-by: Konstantin Komarov +--- + src/lib/decompress_common.c | 13 ------------- + src/lib/decompress_common.h | 14 -------------- + src/lib/lzx_decompress.c | 13 ------------- + src/lib/xpress_decompress.c | 13 ------------- + 4 files changed, 53 deletions(-) + +diff --git a/src/lib/decompress_common.c b/src/lib/decompress_common.c +index 850d8e8c8f1fc75aa46e8f236b0c682daaf30d8f..e96652240859b1dc96720176639526c2aa611398 100644 +--- a/src/lib/decompress_common.c ++++ b/src/lib/decompress_common.c +@@ -3,19 +3,6 @@ + * decompress_common.c - Code shared by the XPRESS and LZX decompressors + * + * Copyright (C) 2015 Eric Biggers +- * +- * This program is free software: you can redistribute it and/or modify it under +- * the terms of the GNU General Public License as published by the Free Software +- * Foundation, either version 2 of the License, or (at your option) any later +- * version. +- * +- * This program is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +- * details. +- * +- * You should have received a copy of the GNU General Public License along with +- * this program. If not, see . + */ + + #include "decompress_common.h" +diff --git a/src/lib/decompress_common.h b/src/lib/decompress_common.h +index 66297f398403f13abe05f3b5af1aa7c5674351e8..2d70ae42f1b511f3b43af7a9b886ac1ffd21822b 100644 +--- a/src/lib/decompress_common.h ++++ b/src/lib/decompress_common.h +@@ -1,22 +1,8 @@ + /* SPDX-License-Identifier: GPL-2.0-or-later */ +- + /* + * decompress_common.h - Code shared by the XPRESS and LZX decompressors + * + * Copyright (C) 2015 Eric Biggers +- * +- * This program is free software: you can redistribute it and/or modify it under +- * the terms of the GNU General Public License as published by the Free Software +- * Foundation, either version 2 of the License, or (at your option) any later +- * version. +- * +- * This program is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +- * details. +- * +- * You should have received a copy of the GNU General Public License along with +- * this program. If not, see . + */ + + #include +diff --git a/src/lib/lzx_decompress.c b/src/lib/lzx_decompress.c +index 77a381a693d117e3e8c4860130880fedf3af868e..6b16f07073c12cf349cfa1cfe6bcbf0168b38d28 100644 +--- a/src/lib/lzx_decompress.c ++++ b/src/lib/lzx_decompress.c +@@ -6,19 +6,6 @@ + * this is the only size used in System Compression. + * + * Copyright (C) 2015 Eric Biggers +- * +- * This program is free software: you can redistribute it and/or modify it under +- * the terms of the GNU General Public License as published by the Free Software +- * Foundation, either version 2 of the License, or (at your option) any later +- * version. +- * +- * This program is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +- * details. +- * +- * You should have received a copy of the GNU General Public License along with +- * this program. If not, see . + */ + + #include "decompress_common.h" +diff --git a/src/lib/xpress_decompress.c b/src/lib/xpress_decompress.c +index 3d98f36a981e672d4d924b5fcbaf655d18d96355..769c6d3dde674f39cc5c088390b69d15fc3dd8b9 100644 +--- a/src/lib/xpress_decompress.c ++++ b/src/lib/xpress_decompress.c +@@ -5,19 +5,6 @@ + * based on the code from wimlib. + * + * Copyright (C) 2015 Eric Biggers +- * +- * This program is free software: you can redistribute it and/or modify it under +- * the terms of the GNU General Public License as published by the Free Software +- * Foundation, either version 2 of the License, or (at your option) any later +- * version. +- * +- * This program is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +- * details. +- * +- * You should have received a copy of the GNU General Public License along with +- * this program. If not, see . + */ + + #include "decompress_common.h" +-- +2.31.1 + diff --git a/SOURCES/0033-fs-ntfs3-Change-how-module-init-info-messages-are-di.patch b/SOURCES/0033-fs-ntfs3-Change-how-module-init-info-messages-are-di.patch new file mode 100644 index 0000000..a576607 --- /dev/null +++ b/SOURCES/0033-fs-ntfs3-Change-how-module-init-info-messages-are-di.patch @@ -0,0 +1,75 @@ +From 2e3a51b59ea26544303e168de8a0479915f09aa3 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Sun, 29 Aug 2021 17:42:39 +0300 +Subject: [Backport 2e3a51b59ea2] src: Change how module init/info + messages are displayed + +Usually in file system init() messages are only displayed in info level. +Change level from notice to info, but keep CONFIG_NTFS3_64BIT_CLUSTER in +notice level. Also this need even more attention so let's put big +warning here so that nobody will not try accidentally use it. + +There is also no good reason to display internal stuff like binary tree +search. This is always on option which can only disabled for debugging +purposes by developer. Also this message does not even check if +developer has disabled it or not so it is useless info. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 27 ++++++++------------------- + 1 file changed, 8 insertions(+), 19 deletions(-) + +diff --git a/src/super.c b/src/super.c +index dbecf095da59268da2fede022b71cb2b40b2d909..55bbc9200a10ebdf3011e0a517eb44afd8a35f3f 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1448,21 +1448,14 @@ static int __init init_ntfs_fs(void) + { + int err; + +- pr_notice("ntfs3: Index binary search\n"); +- pr_notice("ntfs3: Hot fix free clusters\n"); +- pr_notice("ntfs3: Max link count %u\n", NTFS_LINK_MAX); ++ pr_info("ntfs3: Max link count %u\n", NTFS_LINK_MAX); + +-#ifdef CONFIG_NTFS3_FS_POSIX_ACL +- pr_notice("ntfs3: Enabled Linux POSIX ACLs support\n"); +-#endif +-#ifdef CONFIG_NTFS3_64BIT_CLUSTER +- pr_notice("ntfs3: Activated 64 bits per cluster\n"); +-#else +- pr_notice("ntfs3: Activated 32 bits per cluster\n"); +-#endif +-#ifdef CONFIG_NTFS3_LZX_XPRESS +- pr_notice("ntfs3: Read-only LZX/Xpress compression included\n"); +-#endif ++ if (IS_ENABLED(CONFIG_NTFS3_FS_POSIX_ACL)) ++ pr_info("ntfs3: Enabled Linux POSIX ACLs support\n"); ++ if (IS_ENABLED(CONFIG_NTFS3_64BIT_CLUSTER)) ++ pr_notice("ntfs3: Warning: Activated 64 bits per cluster. Windows does not support this\n"); ++ if (IS_ENABLED(CONFIG_NTFS3_LZX_XPRESS)) ++ pr_info("ntfs3: Read-only LZX/Xpress compression included\n"); + + err = ntfs3_init_bitmap(); + if (err) +@@ -1502,15 +1495,11 @@ static void __exit exit_ntfs_fs(void) + + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("ntfs3 read/write filesystem"); +-MODULE_INFO(behaviour, "Index binary search"); +-MODULE_INFO(behaviour, "Hot fix free clusters"); + #ifdef CONFIG_NTFS3_FS_POSIX_ACL + MODULE_INFO(behaviour, "Enabled Linux POSIX ACLs support"); + #endif + #ifdef CONFIG_NTFS3_64BIT_CLUSTER +-MODULE_INFO(cluster, "Activated 64 bits per cluster"); +-#else +-MODULE_INFO(cluster, "Activated 32 bits per cluster"); ++MODULE_INFO(cluster, "Warning: Activated 64 bits per cluster. Windows does not support this"); + #endif + #ifdef CONFIG_NTFS3_LZX_XPRESS + MODULE_INFO(compression, "Read-only lzx/xpress compression included"); +-- +2.31.1 + diff --git a/SOURCES/0034-fs-ntfs3-Remove-unnecesarry-mount-option-noatime.patch b/SOURCES/0034-fs-ntfs3-Remove-unnecesarry-mount-option-noatime.patch new file mode 100644 index 0000000..ec8030e --- /dev/null +++ b/SOURCES/0034-fs-ntfs3-Remove-unnecesarry-mount-option-noatime.patch @@ -0,0 +1,63 @@ +From b8a30b4171b9a3c22ef0605ed74a21544d00c680 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:49 +0300 +Subject: [Backport b8a30b4171b9] src: Remove unnecesarry mount option + noatime +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove unnecesarry mount option noatime because this will be handled +by VFS. Our option parser will never get opt like this. + +Acked-by: Christian Brauner +Reviewed-by: Christoph Hellwig +Reviewed-by: Pali Rohár +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 55bbc9200a10ebdf3011e0a517eb44afd8a35f3f..a18b99a3e3b57b63aa18c18d8183bebfebd8788e 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -223,7 +223,6 @@ enum Opt { + Opt_nohidden, + Opt_showmeta, + Opt_acl, +- Opt_noatime, + Opt_nls, + Opt_prealloc, + Opt_no_acs_rules, +@@ -242,7 +241,6 @@ static const match_table_t ntfs_tokens = { + { Opt_sparse, "sparse" }, + { Opt_nohidden, "nohidden" }, + { Opt_acl, "acl" }, +- { Opt_noatime, "noatime" }, + { Opt_showmeta, "showmeta" }, + { Opt_nls, "nls=%s" }, + { Opt_prealloc, "prealloc" }, +@@ -333,9 +331,6 @@ static noinline int ntfs_parse_options(struct super_block *sb, char *options, + ntfs_err(sb, "support for ACL not compiled in!"); + return -EINVAL; + #endif +- case Opt_noatime: +- sb->s_flags |= SB_NOATIME; +- break; + case Opt_showmeta: + opts->showmeta = 1; + break; +@@ -587,8 +582,6 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + seq_puts(m, ",prealloc"); + if (sb->s_flags & SB_POSIXACL) + seq_puts(m, ",acl"); +- if (sb->s_flags & SB_NOATIME) +- seq_puts(m, ",noatime"); + + return 0; + } +-- +2.31.1 + diff --git a/SOURCES/0035-fs-ntfs3-Remove-unnecesarry-remount-flag-handling.patch b/SOURCES/0035-fs-ntfs3-Remove-unnecesarry-remount-flag-handling.patch new file mode 100644 index 0000000..3043a93 --- /dev/null +++ b/SOURCES/0035-fs-ntfs3-Remove-unnecesarry-remount-flag-handling.patch @@ -0,0 +1,40 @@ +From c2c389fd6c6b0393549578997744b03822dd2b24 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:50 +0300 +Subject: [Backport c2c389fd6c6b] src: Remove unnecesarry remount flag + handling + +Remove unnecesarry remount flag handling. This does not do anything for +this driver. We have already set SB_NODIRATIME when we fill super. Also +noatime should be set from mount option. Now for some reson we try to +set it when remounting. + +Lazytime part looks like it is copied from f2fs and there is own mount +parameter for it. That is why they use it. We do not set lazytime +anywhere in our code. So basically this just blocks lazytime when +remounting. + +Acked-by: Christian Brauner +Reviewed-by: Christoph Hellwig +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/super.c b/src/super.c +index a18b99a3e3b57b63aa18c18d8183bebfebd8788e..6cb689605089e2a592143a2b4154a97211f39c6c 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -417,8 +417,6 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *data) + + clear_mount_options(&old_opts); + +- *flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME) | +- SB_NODIRATIME | SB_NOATIME; + ntfs_info(sb, "re-mounted. Opts: %s", orig_data); + err = 0; + goto out; +-- +2.31.1 + diff --git a/SOURCES/0036-fs-ntfs3-Convert-mount-options-to-pointer-in-sbi.patch b/SOURCES/0036-fs-ntfs3-Convert-mount-options-to-pointer-in-sbi.patch new file mode 100644 index 0000000..f8a758a --- /dev/null +++ b/SOURCES/0036-fs-ntfs3-Convert-mount-options-to-pointer-in-sbi.patch @@ -0,0 +1,275 @@ +From 564c97bdfa39c7d1f331841fb24da6e714693037 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:51 +0300 +Subject: [Backport 564c97bdfa39] src: Convert mount options to pointer in + sbi + +Use pointer to mount options. We want to do this because we will use new +mount api which will benefit that we have spi and mount options in +different allocations. When we remount we do not have to make whole new +spi it is enough that we will allocate just mount options. + +Please note that we can do example remount lot cleaner but things will +change in next patch so this should be just functional. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 2 +- + src/dir.c | 8 ++++---- + src/file.c | 4 ++-- + src/inode.c | 12 ++++++------ + src/ntfs_fs.h | 2 +- + src/super.c | 31 +++++++++++++++++++------------ + src/xattr.c | 2 +- + 7 files changed, 34 insertions(+), 27 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index 34c4cbf7e29bc49bdb3fa060fd2c235c3fb68536..b1055b284c60323168be99ce69b18e7dbd7bb969 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -529,7 +529,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + } else if (pre_alloc == -1) { + pre_alloc = 0; + if (type == ATTR_DATA && !name_len && +- sbi->options.prealloc) { ++ sbi->options->prealloc) { + CLST new_alen2 = bytes_to_cluster( + sbi, get_pre_allocated(new_size)); + pre_alloc = new_alen2 - new_alen; +diff --git a/src/dir.c b/src/dir.c +index 93f6d485564e0103e56ad417f184f1ace0926eea..40440df021eff2a95a87ff7a2af38345a7c40793 100644 +--- a/src/dir.c ++++ b/src/dir.c +@@ -24,7 +24,7 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + int ret, uni_len, warn; + const __le16 *ip; + u8 *op; +- struct nls_table *nls = sbi->options.nls; ++ struct nls_table *nls = sbi->options->nls; + + static_assert(sizeof(wchar_t) == sizeof(__le16)); + +@@ -186,7 +186,7 @@ int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, + { + int ret, slen; + const u8 *end; +- struct nls_table *nls = sbi->options.nls; ++ struct nls_table *nls = sbi->options->nls; + u16 *uname = uni->name; + + static_assert(sizeof(wchar_t) == sizeof(u16)); +@@ -301,10 +301,10 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + return 0; + + /* Skip meta files. Unless option to show metafiles is set. */ +- if (!sbi->options.showmeta && ntfs_is_meta_file(sbi, ino)) ++ if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino)) + return 0; + +- if (sbi->options.nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) ++ if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) + return 0; + + name_len = ntfs_utf16_to_nls(sbi, (struct le_str *)&fname->name_len, +diff --git a/src/file.c b/src/file.c +index 424450e77ad52ae550e8de444dc393411d97cde6..fef57141b161747101d6898b8da774a372de5cbf 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -737,7 +737,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + umode_t mode = inode->i_mode; + int err; + +- if (sbi->options.no_acs_rules) { ++ if (sbi->options->no_acs_rules) { + /* "No access rules" - Force any changes of time etc. */ + attr->ia_valid |= ATTR_FORCE; + /* and disable for editing some attributes. */ +@@ -1185,7 +1185,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file) + int err = 0; + + /* If we are last writer on the inode, drop the block reservation. */ +- if (sbi->options.prealloc && ((file->f_mode & FMODE_WRITE) && ++ if (sbi->options->prealloc && ((file->f_mode & FMODE_WRITE) && + atomic_read(&inode->i_writecount) == 1)) { + ni_lock(ni); + down_write(&ni->file.run_lock); +diff --git a/src/inode.c b/src/inode.c +index db2a5a4c38e4d62abfb2c7ca16947cd766982b13..9f740fd301b24f2b1a67da0b78d091b3a3ae4705 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -49,8 +49,8 @@ static struct inode *ntfs_read_mft(struct inode *inode, + + inode->i_op = NULL; + /* Setup 'uid' and 'gid' */ +- inode->i_uid = sbi->options.fs_uid; +- inode->i_gid = sbi->options.fs_gid; ++ inode->i_uid = sbi->options->fs_uid; ++ inode->i_gid = sbi->options->fs_gid; + + err = mi_init(&ni->mi, sbi, ino); + if (err) +@@ -229,7 +229,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + t32 = le16_to_cpu(attr->nres.run_off); + } + +- mode = S_IFREG | (0777 & sbi->options.fs_fmask_inv); ++ mode = S_IFREG | (0777 & sbi->options->fs_fmask_inv); + + if (!attr->non_res) { + ni->ni_flags |= NI_FLAG_RESIDENT; +@@ -272,7 +272,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + goto out; + + mode = sb->s_root +- ? (S_IFDIR | (0777 & sbi->options.fs_dmask_inv)) ++ ? (S_IFDIR | (0777 & sbi->options->fs_dmask_inv)) + : (S_IFDIR | 0777); + goto next_attr; + +@@ -443,7 +443,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + goto out; + } + +- if ((sbi->options.sys_immutable && ++ if ((sbi->options->sys_immutable && + (std5->fa & FILE_ATTRIBUTE_SYSTEM)) && + !S_ISFIFO(mode) && !S_ISSOCK(mode) && !S_ISLNK(mode)) { + inode->i_flags |= S_IMMUTABLE; +@@ -1244,7 +1244,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + * } + */ + } else if (S_ISREG(mode)) { +- if (sbi->options.sparse) { ++ if (sbi->options->sparse) { + /* Sparsed regular file, cause option 'sparse'. */ + fa = FILE_ATTRIBUTE_SPARSE_FILE | + FILE_ATTRIBUTE_ARCHIVE; +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 97e682ebcfb94dd1ad9677ed5ee82bb6a88401a5..98c90c399ee2f447f3ad5f0847c56ca7561f17fb 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -279,7 +279,7 @@ struct ntfs_sb_info { + #endif + } compress; + +- struct ntfs_mount_options options; ++ struct ntfs_mount_options *options; + struct ratelimit_state msg_ratelimit; + }; + +diff --git a/src/super.c b/src/super.c +index 6cb689605089e2a592143a2b4154a97211f39c6c..0f38203420515b6386bdd51e83c402c431743946 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -389,11 +389,11 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *data) + return -ENOMEM; + + /* Store original options. */ +- memcpy(&old_opts, &sbi->options, sizeof(old_opts)); +- clear_mount_options(&sbi->options); +- memset(&sbi->options, 0, sizeof(sbi->options)); ++ memcpy(&old_opts, sbi->options, sizeof(old_opts)); ++ clear_mount_options(sbi->options); ++ memset(sbi->options, 0, sizeof(old_opts)); + +- err = ntfs_parse_options(sb, data, 0, &sbi->options); ++ err = ntfs_parse_options(sb, data, 0, sbi->options); + if (err) + goto restore_opts; + +@@ -409,7 +409,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *data) + sync_filesystem(sb); + + if (ro_rw && (sbi->volume.flags & VOLUME_FLAG_DIRTY) && +- !sbi->options.force) { ++ !sbi->options->force) { + ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!"); + err = -EINVAL; + goto restore_opts; +@@ -422,8 +422,8 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *data) + goto out; + + restore_opts: +- clear_mount_options(&sbi->options); +- memcpy(&sbi->options, &old_opts, sizeof(old_opts)); ++ clear_mount_options(sbi->options); ++ memcpy(sbi->options, &old_opts, sizeof(old_opts)); + + out: + kfree(orig_data); +@@ -506,7 +506,8 @@ static noinline void put_ntfs(struct ntfs_sb_info *sbi) + xpress_free_decompressor(sbi->compress.xpress); + lzx_free_decompressor(sbi->compress.lzx); + #endif +- clear_mount_options(&sbi->options); ++ clear_mount_options(sbi->options); ++ kfree(sbi->options); + + kfree(sbi); + } +@@ -545,7 +546,7 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + { + struct super_block *sb = root->d_sb; + struct ntfs_sb_info *sbi = sb->s_fs_info; +- struct ntfs_mount_options *opts = &sbi->options; ++ struct ntfs_mount_options *opts = sbi->options; + struct user_namespace *user_ns = seq_user_ns(m); + + if (opts->uid) +@@ -930,6 +931,12 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + if (!sbi) + return -ENOMEM; + ++ sbi->options = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS); ++ if (!sbi->options) { ++ kfree(sbi); ++ return -ENOMEM; ++ } ++ + sb->s_fs_info = sbi; + sbi->sb = sb; + sb->s_flags |= SB_NODIRATIME; +@@ -942,7 +949,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + +- err = ntfs_parse_options(sb, data, silent, &sbi->options); ++ err = ntfs_parse_options(sb, data, silent, sbi->options); + if (err) + goto out; + +@@ -1074,7 +1081,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + goto out; + } + } else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) { +- if (!is_ro && !sbi->options.force) { ++ if (!is_ro && !sbi->options->force) { + ntfs_warn( + sb, + "volume is dirty and \"force\" flag is not set!"); +@@ -1394,7 +1401,7 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len) + if (sbi->flags & NTFS_FLAGS_NODISCARD) + return -EOPNOTSUPP; + +- if (!sbi->options.discard) ++ if (!sbi->options->discard) + return -EOPNOTSUPP; + + lbo = (u64)lcn << sbi->cluster_bits; +diff --git a/src/xattr.c b/src/xattr.c +index b15d532e4a17087b06284d82965c21224f1d4f35..ac4b37bf8832a51b9e32b7c4feb6f1c762d5b271 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -769,7 +769,7 @@ int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) + int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, + int mask) + { +- if (ntfs_sb(inode->i_sb)->options.no_acs_rules) { ++ if (ntfs_sb(inode->i_sb)->options->no_acs_rules) { + /* "No access rules" mode - Allow all changes. */ + return 0; + } +-- +2.31.1 + diff --git a/SOURCES/0037-fs-ntfs3-Use-new-api-for-mounting.patch b/SOURCES/0037-fs-ntfs3-Use-new-api-for-mounting.patch new file mode 100644 index 0000000..7a05a44 --- /dev/null +++ b/SOURCES/0037-fs-ntfs3-Use-new-api-for-mounting.patch @@ -0,0 +1,600 @@ +From 610f8f5a7baf998e70a61c63e53869b676d9b04c Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:52 +0300 +Subject: [Backport 610f8f5a7baf] src: Use new api for mounting + +We have now new mount api as described in Documentation/filesystems. We +should use it as it gives us some benefits which are desribed here +lore.kernel.org/linux-fsdevel/159646178122.1784947.11705396571718464082.stgit@warthog.procyon.org.uk/ + +Nls loading is changed a to load with string. This did make code also +little cleaner. + +Also try to use fsparam_flag_no as much as possible. This is just nice +little touch and is not mandatory but it should not make any harm. It +is just convenient that we can use example acl/noacl mount options. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/ntfs_fs.h | 1 + + src/super.c | 433 ++++++++++++++++++++++++--------------------- + 2 files changed, 229 insertions(+), 205 deletions(-) + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 98c90c399ee2f447f3ad5f0847c56ca7561f17fb..aa18f12b7096e2ecd7e6db365aaa0152d4fb2327 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -52,6 +52,7 @@ + // clang-format on + + struct ntfs_mount_options { ++ char *nls_name; + struct nls_table *nls; + + kuid_t fs_uid; +diff --git a/src/super.c b/src/super.c +index 0f38203420515b6386bdd51e83c402c431743946..befa78d3cb26767e401c84cc87a40006c4603186 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -28,11 +28,12 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include + #include +-#include + #include + #include + +@@ -205,9 +206,11 @@ void *ntfs_put_shared(void *ptr) + return ret; + } + +-static inline void clear_mount_options(struct ntfs_mount_options *options) ++static inline void put_mount_options(struct ntfs_mount_options *options) + { ++ kfree(options->nls_name); + unload_nls(options->nls); ++ kfree(options); + } + + enum Opt { +@@ -229,205 +232,171 @@ enum Opt { + Opt_err, + }; + +-static const match_table_t ntfs_tokens = { +- { Opt_uid, "uid=%u" }, +- { Opt_gid, "gid=%u" }, +- { Opt_umask, "umask=%o" }, +- { Opt_dmask, "dmask=%o" }, +- { Opt_fmask, "fmask=%o" }, +- { Opt_immutable, "sys_immutable" }, +- { Opt_discard, "discard" }, +- { Opt_force, "force" }, +- { Opt_sparse, "sparse" }, +- { Opt_nohidden, "nohidden" }, +- { Opt_acl, "acl" }, +- { Opt_showmeta, "showmeta" }, +- { Opt_nls, "nls=%s" }, +- { Opt_prealloc, "prealloc" }, +- { Opt_no_acs_rules, "no_acs_rules" }, +- { Opt_err, NULL }, ++static const struct fs_parameter_spec ntfs_fs_parameters[] = { ++ fsparam_u32("uid", Opt_uid), ++ fsparam_u32("gid", Opt_gid), ++ fsparam_u32oct("umask", Opt_umask), ++ fsparam_u32oct("dmask", Opt_dmask), ++ fsparam_u32oct("fmask", Opt_fmask), ++ fsparam_flag_no("sys_immutable", Opt_immutable), ++ fsparam_flag_no("discard", Opt_discard), ++ fsparam_flag_no("force", Opt_force), ++ fsparam_flag_no("sparse", Opt_sparse), ++ fsparam_flag("nohidden", Opt_nohidden), ++ fsparam_flag_no("acl", Opt_acl), ++ fsparam_flag_no("showmeta", Opt_showmeta), ++ fsparam_string("nls", Opt_nls), ++ fsparam_flag_no("prealloc", Opt_prealloc), ++ fsparam_flag("no_acs_rules", Opt_no_acs_rules), ++ {} + }; + +-static noinline int ntfs_parse_options(struct super_block *sb, char *options, +- int silent, +- struct ntfs_mount_options *opts) ++/* ++ * Load nls table or if @nls is utf8 then return NULL. ++ */ ++static struct nls_table *ntfs_load_nls(char *nls) + { +- char *p; +- substring_t args[MAX_OPT_ARGS]; +- int option; +- char nls_name[30]; +- struct nls_table *nls; ++ struct nls_table *ret; + +- opts->fs_uid = current_uid(); +- opts->fs_gid = current_gid(); +- opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask(); +- nls_name[0] = 0; ++ if (!nls) ++ nls = CONFIG_NLS_DEFAULT; + +- if (!options) +- goto out; ++ if (strcmp(nls, "utf8") == 0) ++ return NULL; + +- while ((p = strsep(&options, ","))) { +- int token; ++ if (strcmp(nls, CONFIG_NLS_DEFAULT) == 0) ++ return load_nls_default(); + +- if (!*p) +- continue; ++ ret = load_nls(nls); ++ if (ret) ++ return ret; + +- token = match_token(p, ntfs_tokens, args); +- switch (token) { +- case Opt_immutable: +- opts->sys_immutable = 1; +- break; +- case Opt_uid: +- if (match_int(&args[0], &option)) +- return -EINVAL; +- opts->fs_uid = make_kuid(current_user_ns(), option); +- if (!uid_valid(opts->fs_uid)) +- return -EINVAL; +- opts->uid = 1; +- break; +- case Opt_gid: +- if (match_int(&args[0], &option)) +- return -EINVAL; +- opts->fs_gid = make_kgid(current_user_ns(), option); +- if (!gid_valid(opts->fs_gid)) +- return -EINVAL; +- opts->gid = 1; +- break; +- case Opt_umask: +- if (match_octal(&args[0], &option)) +- return -EINVAL; +- opts->fs_fmask_inv = opts->fs_dmask_inv = ~option; +- opts->fmask = opts->dmask = 1; +- break; +- case Opt_dmask: +- if (match_octal(&args[0], &option)) +- return -EINVAL; +- opts->fs_dmask_inv = ~option; +- opts->dmask = 1; +- break; +- case Opt_fmask: +- if (match_octal(&args[0], &option)) +- return -EINVAL; +- opts->fs_fmask_inv = ~option; +- opts->fmask = 1; +- break; +- case Opt_discard: +- opts->discard = 1; +- break; +- case Opt_force: +- opts->force = 1; +- break; +- case Opt_sparse: +- opts->sparse = 1; +- break; +- case Opt_nohidden: +- opts->nohidden = 1; +- break; +- case Opt_acl: ++ return ERR_PTR(-EINVAL); ++} ++ ++static int ntfs_fs_parse_param(struct fs_context *fc, ++ struct fs_parameter *param) ++{ ++ struct ntfs_mount_options *opts = fc->fs_private; ++ struct fs_parse_result result; ++ int opt; ++ ++ opt = fs_parse(fc, ntfs_fs_parameters, param, &result); ++ if (opt < 0) ++ return opt; ++ ++ switch (opt) { ++ case Opt_uid: ++ opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); ++ if (!uid_valid(opts->fs_uid)) ++ return invalf(fc, "ntfs3: Invalid value for uid."); ++ opts->uid = 1; ++ break; ++ case Opt_gid: ++ opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); ++ if (!gid_valid(opts->fs_gid)) ++ return invalf(fc, "ntfs3: Invalid value for gid."); ++ opts->gid = 1; ++ break; ++ case Opt_umask: ++ if (result.uint_32 & ~07777) ++ return invalf(fc, "ntfs3: Invalid value for umask."); ++ opts->fs_fmask_inv = ~result.uint_32; ++ opts->fs_dmask_inv = ~result.uint_32; ++ opts->fmask = 1; ++ opts->dmask = 1; ++ break; ++ case Opt_dmask: ++ if (result.uint_32 & ~07777) ++ return invalf(fc, "ntfs3: Invalid value for dmask."); ++ opts->fs_dmask_inv = ~result.uint_32; ++ opts->dmask = 1; ++ break; ++ case Opt_fmask: ++ if (result.uint_32 & ~07777) ++ return invalf(fc, "ntfs3: Invalid value for fmask."); ++ opts->fs_fmask_inv = ~result.uint_32; ++ opts->fmask = 1; ++ break; ++ case Opt_immutable: ++ opts->sys_immutable = result.negated ? 0 : 1; ++ break; ++ case Opt_discard: ++ opts->discard = result.negated ? 0 : 1; ++ break; ++ case Opt_force: ++ opts->force = result.negated ? 0 : 1; ++ break; ++ case Opt_sparse: ++ opts->sparse = result.negated ? 0 : 1; ++ break; ++ case Opt_nohidden: ++ opts->nohidden = 1; ++ break; ++ case Opt_acl: ++ if (!result.negated) + #ifdef CONFIG_NTFS3_FS_POSIX_ACL +- sb->s_flags |= SB_POSIXACL; +- break; ++ fc->sb_flags |= SB_POSIXACL; + #else +- ntfs_err(sb, "support for ACL not compiled in!"); +- return -EINVAL; ++ return invalf(fc, "ntfs3: Support for ACL not compiled in!"); + #endif +- case Opt_showmeta: +- opts->showmeta = 1; +- break; +- case Opt_nls: +- match_strlcpy(nls_name, &args[0], sizeof(nls_name)); +- break; +- case Opt_prealloc: +- opts->prealloc = 1; +- break; +- case Opt_no_acs_rules: +- opts->no_acs_rules = 1; +- break; +- default: +- if (!silent) +- ntfs_err( +- sb, +- "Unrecognized mount option \"%s\" or missing value", +- p); +- //return -EINVAL; +- } +- } +- +-out: +- if (!strcmp(nls_name[0] ? nls_name : CONFIG_NLS_DEFAULT, "utf8")) { +- /* +- * For UTF-8 use utf16s_to_utf8s()/utf8s_to_utf16s() +- * instead of NLS. +- */ +- nls = NULL; +- } else if (nls_name[0]) { +- nls = load_nls(nls_name); +- if (!nls) { +- ntfs_err(sb, "failed to load \"%s\"", nls_name); +- return -EINVAL; +- } +- } else { +- nls = load_nls_default(); +- if (!nls) { +- ntfs_err(sb, "failed to load default nls"); +- return -EINVAL; +- } ++ else ++ fc->sb_flags &= ~SB_POSIXACL; ++ break; ++ case Opt_showmeta: ++ opts->showmeta = result.negated ? 0 : 1; ++ break; ++ case Opt_nls: ++ kfree(opts->nls_name); ++ opts->nls_name = param->string; ++ param->string = NULL; ++ break; ++ case Opt_prealloc: ++ opts->prealloc = result.negated ? 0 : 1; ++ break; ++ case Opt_no_acs_rules: ++ opts->no_acs_rules = 1; ++ break; ++ default: ++ /* Should not be here unless we forget add case. */ ++ return -EINVAL; + } +- opts->nls = nls; +- + return 0; + } + +-static int ntfs_remount(struct super_block *sb, int *flags, char *data) ++static int ntfs_fs_reconfigure(struct fs_context *fc) + { +- int err, ro_rw; ++ struct super_block *sb = fc->root->d_sb; + struct ntfs_sb_info *sbi = sb->s_fs_info; +- struct ntfs_mount_options old_opts; +- char *orig_data = kstrdup(data, GFP_KERNEL); +- +- if (data && !orig_data) +- return -ENOMEM; +- +- /* Store original options. */ +- memcpy(&old_opts, sbi->options, sizeof(old_opts)); +- clear_mount_options(sbi->options); +- memset(sbi->options, 0, sizeof(old_opts)); +- +- err = ntfs_parse_options(sb, data, 0, sbi->options); +- if (err) +- goto restore_opts; ++ struct ntfs_mount_options *new_opts = fc->fs_private; ++ int ro_rw; + +- ro_rw = sb_rdonly(sb) && !(*flags & SB_RDONLY); ++ ro_rw = sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY); + if (ro_rw && (sbi->flags & NTFS_FLAGS_NEED_REPLAY)) { +- ntfs_warn( +- sb, +- "Couldn't remount rw because journal is not replayed. Please umount/remount instead\n"); +- err = -EINVAL; +- goto restore_opts; ++ errorf(fc, "ntfs3: Couldn't remount rw because journal is not replayed. Please umount/remount instead\n"); ++ return -EINVAL; ++ } ++ ++ new_opts->nls = ntfs_load_nls(new_opts->nls_name); ++ if (IS_ERR(new_opts->nls)) { ++ new_opts->nls = NULL; ++ errorf(fc, "ntfs3: Cannot load nls %s", new_opts->nls_name); ++ return -EINVAL; + } ++ if (new_opts->nls != sbi->options->nls) ++ return invalf(fc, "ntfs3: Cannot use different nls when remounting!"); + + sync_filesystem(sb); + + if (ro_rw && (sbi->volume.flags & VOLUME_FLAG_DIRTY) && +- !sbi->options->force) { +- ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!"); +- err = -EINVAL; +- goto restore_opts; ++ !new_opts->force) { ++ errorf(fc, "ntfs3: Volume is dirty and \"force\" flag is not set!"); ++ return -EINVAL; + } + +- clear_mount_options(&old_opts); +- +- ntfs_info(sb, "re-mounted. Opts: %s", orig_data); +- err = 0; +- goto out; +- +-restore_opts: +- clear_mount_options(sbi->options); +- memcpy(sbi->options, &old_opts, sizeof(old_opts)); ++ memcpy(sbi->options, new_opts, sizeof(*new_opts)); + +-out: +- kfree(orig_data); +- return err; ++ return 0; + } + + static struct kmem_cache *ntfs_inode_cachep; +@@ -506,9 +475,6 @@ static noinline void put_ntfs(struct ntfs_sb_info *sbi) + xpress_free_decompressor(sbi->compress.xpress); + lzx_free_decompressor(sbi->compress.lzx); + #endif +- clear_mount_options(sbi->options); +- kfree(sbi->options); +- + kfree(sbi); + } + +@@ -519,7 +485,9 @@ static void ntfs_put_super(struct super_block *sb) + /* Mark rw ntfs as clear, if possible. */ + ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); + ++ put_mount_options(sbi->options); + put_ntfs(sbi); ++ sb->s_fs_info = NULL; + + sync_blockdev(sb->s_bdev); + } +@@ -635,7 +603,6 @@ static const struct super_operations ntfs_sops = { + .statfs = ntfs_statfs, + .show_options = ntfs_show_options, + .sync_fs = ntfs_sync_fs, +- .remount_fs = ntfs_remount, + .write_inode = ntfs3_write_inode, + }; + +@@ -905,10 +872,10 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + /* + * ntfs_fill_super - Try to mount. + */ +-static int ntfs_fill_super(struct super_block *sb, void *data, int silent) ++static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + { + int err; +- struct ntfs_sb_info *sbi; ++ struct ntfs_sb_info *sbi = sb->s_fs_info; + struct block_device *bdev = sb->s_bdev; + struct inode *bd_inode = bdev->bd_inode; + struct request_queue *rq = bdev_get_queue(bdev); +@@ -927,17 +894,6 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + + ref.high = 0; + +- sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS); +- if (!sbi) +- return -ENOMEM; +- +- sbi->options = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS); +- if (!sbi->options) { +- kfree(sbi); +- return -ENOMEM; +- } +- +- sb->s_fs_info = sbi; + sbi->sb = sb; + sb->s_flags |= SB_NODIRATIME; + sb->s_magic = 0x7366746e; // "ntfs" +@@ -949,9 +905,12 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + +- err = ntfs_parse_options(sb, data, silent, sbi->options); +- if (err) +- goto out; ++ sbi->options->nls = ntfs_load_nls(sbi->options->nls_name); ++ if (IS_ERR(sbi->options->nls)) { ++ sbi->options->nls = NULL; ++ errorf(fc, "Cannot load nls %s", sbi->options->nls_name); ++ return -EINVAL; ++ } + + if (!rq || !blk_queue_discard(rq) || !rq->limits.discard_granularity) { + ; +@@ -1344,6 +1303,9 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + goto out; + } + ++ fc->fs_private = NULL; ++ fc->s_fs_info = NULL; ++ + return 0; + + out: +@@ -1354,9 +1316,6 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent) + sb->s_root = NULL; + } + +- put_ntfs(sbi); +- +- sb->s_fs_info = NULL; + return err; + } + +@@ -1426,19 +1385,83 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len) + return err; + } + +-static struct dentry *ntfs_mount(struct file_system_type *fs_type, int flags, +- const char *dev_name, void *data) ++static int ntfs_fs_get_tree(struct fs_context *fc) ++{ ++ return get_tree_bdev(fc, ntfs_fill_super); ++} ++ ++/* ++ * ntfs_fs_free - Free fs_context. ++ * ++ * Note that this will be called after fill_super and reconfigure ++ * even when they pass. So they have to take pointers if they pass. ++ */ ++static void ntfs_fs_free(struct fs_context *fc) ++{ ++ struct ntfs_mount_options *opts = fc->fs_private; ++ struct ntfs_sb_info *sbi = fc->s_fs_info; ++ ++ if (sbi) ++ put_ntfs(sbi); ++ ++ if (opts) ++ put_mount_options(opts); ++} ++ ++static const struct fs_context_operations ntfs_context_ops = { ++ .parse_param = ntfs_fs_parse_param, ++ .get_tree = ntfs_fs_get_tree, ++ .reconfigure = ntfs_fs_reconfigure, ++ .free = ntfs_fs_free, ++}; ++ ++/* ++ * ntfs_init_fs_context - Initialize spi and opts ++ * ++ * This will called when mount/remount. We will first initiliaze ++ * options so that if remount we can use just that. ++ */ ++static int ntfs_init_fs_context(struct fs_context *fc) + { +- return mount_bdev(fs_type, flags, dev_name, data, ntfs_fill_super); ++ struct ntfs_mount_options *opts; ++ struct ntfs_sb_info *sbi; ++ ++ opts = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS); ++ if (!opts) ++ return -ENOMEM; ++ ++ /* Default options. */ ++ opts->fs_uid = current_uid(); ++ opts->fs_gid = current_gid(); ++ opts->fs_fmask_inv = ~current_umask(); ++ opts->fs_dmask_inv = ~current_umask(); ++ ++ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) ++ goto ok; ++ ++ sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS); ++ if (!sbi) { ++ kfree(opts); ++ return -ENOMEM; ++ } ++ ++ sbi->options = opts; ++ fc->s_fs_info = sbi; ++ok: ++ fc->fs_private = opts; ++ fc->ops = &ntfs_context_ops; ++ ++ return 0; + } + + // clang-format off + static struct file_system_type ntfs_fs_type = { +- .owner = THIS_MODULE, +- .name = "ntfs3", +- .mount = ntfs_mount, +- .kill_sb = kill_block_super, +- .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, ++ .owner = THIS_MODULE, ++ .name = "ntfs3", ++ .init_fs_context = ntfs_init_fs_context, ++ .parameters = ntfs_fs_parameters, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, + }; + // clang-format on + +-- +2.31.1 + diff --git a/SOURCES/0038-fs-ntfs3-Init-spi-more-in-init_fs_context-than-fill_.patch b/SOURCES/0038-fs-ntfs3-Init-spi-more-in-init_fs_context-than-fill_.patch new file mode 100644 index 0000000..98468cf --- /dev/null +++ b/SOURCES/0038-fs-ntfs3-Init-spi-more-in-init_fs_context-than-fill_.patch @@ -0,0 +1,105 @@ +From 27fac77707a1d99deef33fd5f3f5f2ed96bfbf6a Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:53 +0300 +Subject: [Backport 27fac77707a1] src: Init spi more in init_fs_context + than fill_super + +init_fs_context() is meant to initialize s_fs_info (spi). Move spi +initializing code there which we can initialize before fill_super(). + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 41 ++++++++++++++++++++++------------------- + 1 file changed, 22 insertions(+), 19 deletions(-) + +diff --git a/src/super.c b/src/super.c +index befa78d3cb26767e401c84cc87a40006c4603186..420cd140917007d9edb3072e19f77c87139f8f20 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -887,7 +887,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + const struct VOLUME_INFO *info; + u32 idx, done, bytes; + struct ATTR_DEF_ENTRY *t; +- u16 *upcase = NULL; ++ u16 *upcase; + u16 *shared; + bool is_ro; + struct MFT_REF ref; +@@ -902,9 +902,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec + sb->s_xattr = ntfs_xattr_handlers; + +- ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL, +- DEFAULT_RATELIMIT_BURST); +- + sbi->options->nls = ntfs_load_nls(sbi->options->nls_name); + if (IS_ERR(sbi->options->nls)) { + sbi->options->nls = NULL; +@@ -934,12 +931,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + sb->s_maxbytes = 0xFFFFFFFFull << sbi->cluster_bits; + #endif + +- mutex_init(&sbi->compress.mtx_lznt); +-#ifdef CONFIG_NTFS3_LZX_XPRESS +- mutex_init(&sbi->compress.mtx_xpress); +- mutex_init(&sbi->compress.mtx_lzx); +-#endif +- + /* + * Load $Volume. This should be done before $LogFile + * 'cause 'sbi->volume.ni' is used 'ntfs_set_state'. +@@ -1224,11 +1215,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + goto out; + } + +- sbi->upcase = upcase = kvmalloc(0x10000 * sizeof(short), GFP_KERNEL); +- if (!upcase) { +- err = -ENOMEM; +- goto out; +- } ++ upcase = sbi->upcase; + + for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) { + const __le16 *src; +@@ -1440,10 +1427,21 @@ static int ntfs_init_fs_context(struct fs_context *fc) + goto ok; + + sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS); +- if (!sbi) { +- kfree(opts); +- return -ENOMEM; +- } ++ if (!sbi) ++ goto free_opts; ++ ++ sbi->upcase = kvmalloc(0x10000 * sizeof(short), GFP_KERNEL); ++ if (!sbi->upcase) ++ goto free_sbi; ++ ++ ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL, ++ DEFAULT_RATELIMIT_BURST); ++ ++ mutex_init(&sbi->compress.mtx_lznt); ++#ifdef CONFIG_NTFS3_LZX_XPRESS ++ mutex_init(&sbi->compress.mtx_xpress); ++ mutex_init(&sbi->compress.mtx_lzx); ++#endif + + sbi->options = opts; + fc->s_fs_info = sbi; +@@ -1452,6 +1450,11 @@ static int ntfs_init_fs_context(struct fs_context *fc) + fc->ops = &ntfs_context_ops; + + return 0; ++free_opts: ++ kfree(opts); ++free_sbi: ++ kfree(sbi); ++ return -ENOMEM; + } + + // clang-format off +-- +2.31.1 + diff --git a/SOURCES/0039-fs-ntfs3-Make-mount-option-nohidden-more-universal.patch b/SOURCES/0039-fs-ntfs3-Make-mount-option-nohidden-more-universal.patch new file mode 100644 index 0000000..d4e1f8b --- /dev/null +++ b/SOURCES/0039-fs-ntfs3-Make-mount-option-nohidden-more-universal.patch @@ -0,0 +1,48 @@ +From 9d1939f4575f3fda70dd94542dbd4d775e104132 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:54 +0300 +Subject: [Backport 9d1939f4575f] src: Make mount option nohidden more + universal +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If we call Opt_nohidden with just keyword hidden, then we can use +hidden/nohidden when mounting. We already use this method for almoust +all other parameters so it is just logical that this will use same +method. + +Acked-by: Christian Brauner +Reviewed-by: Christoph Hellwig +Reviewed-by: Pali Rohár +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 420cd140917007d9edb3072e19f77c87139f8f20..729ead6f2fac0883b7c1180e45d240f35ba7117b 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -242,7 +242,7 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { + fsparam_flag_no("discard", Opt_discard), + fsparam_flag_no("force", Opt_force), + fsparam_flag_no("sparse", Opt_sparse), +- fsparam_flag("nohidden", Opt_nohidden), ++ fsparam_flag_no("hidden", Opt_nohidden), + fsparam_flag_no("acl", Opt_acl), + fsparam_flag_no("showmeta", Opt_showmeta), + fsparam_string("nls", Opt_nls), +@@ -331,7 +331,7 @@ static int ntfs_fs_parse_param(struct fs_context *fc, + opts->sparse = result.negated ? 0 : 1; + break; + case Opt_nohidden: +- opts->nohidden = 1; ++ opts->nohidden = result.negated ? 1 : 0; + break; + case Opt_acl: + if (!result.negated) +-- +2.31.1 + diff --git a/SOURCES/0040-fs-ntfs3-Add-iocharset-mount-option-as-alias-for-nls.patch b/SOURCES/0040-fs-ntfs3-Add-iocharset-mount-option-as-alias-for-nls.patch new file mode 100644 index 0000000..d08d966 --- /dev/null +++ b/SOURCES/0040-fs-ntfs3-Add-iocharset-mount-option-as-alias-for-nls.patch @@ -0,0 +1,85 @@ +From e274cde8c7550cac46eb7aba3a77aff44ae0b301 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:55 +0300 +Subject: [Backport e274cde8c755] src: Add iocharset= mount option as + alias for nls= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Other fs drivers are using iocharset= mount option for specifying charset. +So add it also for ntfs3 and mark old nls= mount option as deprecated. + +Reviewed-by: Pali Rohár +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 729ead6f2fac0883b7c1180e45d240f35ba7117b..503e2e23f711e9c66ede31dde62ac64ca3c44f76 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -226,7 +226,7 @@ enum Opt { + Opt_nohidden, + Opt_showmeta, + Opt_acl, +- Opt_nls, ++ Opt_iocharset, + Opt_prealloc, + Opt_no_acs_rules, + Opt_err, +@@ -245,9 +245,13 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { + fsparam_flag_no("hidden", Opt_nohidden), + fsparam_flag_no("acl", Opt_acl), + fsparam_flag_no("showmeta", Opt_showmeta), +- fsparam_string("nls", Opt_nls), + fsparam_flag_no("prealloc", Opt_prealloc), + fsparam_flag("no_acs_rules", Opt_no_acs_rules), ++ fsparam_string("iocharset", Opt_iocharset), ++ ++ __fsparam(fs_param_is_string, ++ "nls", Opt_iocharset, ++ fs_param_deprecated, NULL), + {} + }; + +@@ -346,7 +350,7 @@ static int ntfs_fs_parse_param(struct fs_context *fc, + case Opt_showmeta: + opts->showmeta = result.negated ? 0 : 1; + break; +- case Opt_nls: ++ case Opt_iocharset: + kfree(opts->nls_name); + opts->nls_name = param->string; + param->string = NULL; +@@ -380,11 +384,11 @@ static int ntfs_fs_reconfigure(struct fs_context *fc) + new_opts->nls = ntfs_load_nls(new_opts->nls_name); + if (IS_ERR(new_opts->nls)) { + new_opts->nls = NULL; +- errorf(fc, "ntfs3: Cannot load nls %s", new_opts->nls_name); ++ errorf(fc, "ntfs3: Cannot load iocharset %s", new_opts->nls_name); + return -EINVAL; + } + if (new_opts->nls != sbi->options->nls) +- return invalf(fc, "ntfs3: Cannot use different nls when remounting!"); ++ return invalf(fc, "ntfs3: Cannot use different iocharset when remounting!"); + + sync_filesystem(sb); + +@@ -528,9 +532,9 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + if (opts->dmask) + seq_printf(m, ",dmask=%04o", ~opts->fs_dmask_inv); + if (opts->nls) +- seq_printf(m, ",nls=%s", opts->nls->charset); ++ seq_printf(m, ",iocharset=%s", opts->nls->charset); + else +- seq_puts(m, ",nls=utf8"); ++ seq_puts(m, ",iocharset=utf8"); + if (opts->sys_immutable) + seq_puts(m, ",sys_immutable"); + if (opts->discard) +-- +2.31.1 + diff --git a/SOURCES/0041-fs-ntfs3-Rename-mount-option-no_acs_rules-no-acsrule.patch b/SOURCES/0041-fs-ntfs3-Rename-mount-option-no_acs_rules-no-acsrule.patch new file mode 100644 index 0000000..1876d60 --- /dev/null +++ b/SOURCES/0041-fs-ntfs3-Rename-mount-option-no_acs_rules-no-acsrule.patch @@ -0,0 +1,106 @@ +From 28a941ffc1404b66d67228cbe8392bbadb94af0d Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:56 +0300 +Subject: [Backport 28a941ffc140] src: Rename mount option no_acs_rules > + (no)acsrules + +Rename mount option no_acs_rules to (no)acsrules. This allow us to use +possibility to mount with options noaclrules or aclrules. + +Acked-by: Christian Brauner +Reviewed-by: Christoph Hellwig +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/file.c | 2 +- + src/ntfs_fs.h | 2 +- + src/super.c | 12 ++++++------ + src/xattr.c | 2 +- + 4 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/src/file.c b/src/file.c +index fef57141b161747101d6898b8da774a372de5cbf..0743d806c567409dd2845b6577f2ae3adf92b383 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -737,7 +737,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + umode_t mode = inode->i_mode; + int err; + +- if (sbi->options->no_acs_rules) { ++ if (sbi->options->noacsrules) { + /* "No access rules" - Force any changes of time etc. */ + attr->ia_valid |= ATTR_FORCE; + /* and disable for editing some attributes. */ +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index aa18f12b7096e2ecd7e6db365aaa0152d4fb2327..15bab48bc1ad13a9bc93d365ce92c8978635ec2b 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -70,7 +70,7 @@ struct ntfs_mount_options { + showmeta : 1, /* Show meta files. */ + nohidden : 1, /* Do not show hidden files. */ + force : 1, /* Rw mount dirty volume. */ +- no_acs_rules : 1, /*Exclude acs rules. */ ++ noacsrules : 1, /*Exclude acs rules. */ + prealloc : 1 /* Preallocate space when file is growing. */ + ; + }; +diff --git a/src/super.c b/src/super.c +index 503e2e23f711e9c66ede31dde62ac64ca3c44f76..0690e7e4f00de4e6c89cacebdb4862bbb8f00bc6 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -228,7 +228,7 @@ enum Opt { + Opt_acl, + Opt_iocharset, + Opt_prealloc, +- Opt_no_acs_rules, ++ Opt_noacsrules, + Opt_err, + }; + +@@ -246,7 +246,7 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { + fsparam_flag_no("acl", Opt_acl), + fsparam_flag_no("showmeta", Opt_showmeta), + fsparam_flag_no("prealloc", Opt_prealloc), +- fsparam_flag("no_acs_rules", Opt_no_acs_rules), ++ fsparam_flag_no("acsrules", Opt_noacsrules), + fsparam_string("iocharset", Opt_iocharset), + + __fsparam(fs_param_is_string, +@@ -358,8 +358,8 @@ static int ntfs_fs_parse_param(struct fs_context *fc, + case Opt_prealloc: + opts->prealloc = result.negated ? 0 : 1; + break; +- case Opt_no_acs_rules: +- opts->no_acs_rules = 1; ++ case Opt_noacsrules: ++ opts->noacsrules = result.negated ? 1 : 0; + break; + default: + /* Should not be here unless we forget add case. */ +@@ -547,8 +547,8 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + seq_puts(m, ",nohidden"); + if (opts->force) + seq_puts(m, ",force"); +- if (opts->no_acs_rules) +- seq_puts(m, ",no_acs_rules"); ++ if (opts->noacsrules) ++ seq_puts(m, ",noacsrules"); + if (opts->prealloc) + seq_puts(m, ",prealloc"); + if (sb->s_flags & SB_POSIXACL) +diff --git a/src/xattr.c b/src/xattr.c +index ac4b37bf8832a51b9e32b7c4feb6f1c762d5b271..6f88cb77a17f666fc7cdcdcdcd44b032ceca1c07 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -769,7 +769,7 @@ int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) + int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, + int mask) + { +- if (ntfs_sb(inode->i_sb)->options->no_acs_rules) { ++ if (ntfs_sb(inode->i_sb)->options->noacsrules) { + /* "No access rules" mode - Allow all changes. */ + return 0; + } +-- +2.31.1 + diff --git a/SOURCES/0042-fs-ntfs3-Show-uid-gid-always-in-show_options.patch b/SOURCES/0042-fs-ntfs3-Show-uid-gid-always-in-show_options.patch new file mode 100644 index 0000000..9fc12de --- /dev/null +++ b/SOURCES/0042-fs-ntfs3-Show-uid-gid-always-in-show_options.patch @@ -0,0 +1,95 @@ +From 15b2ae776044ac52cddda8a3e6c9fecd15226b8c Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 18:35:57 +0300 +Subject: [Backport 15b2ae776044] src: Show uid/gid always in + show_options() + +Show options should show option according documentation when some value +is not default or when ever coder wants. Uid/gid are problematic because +it is hard to know which are defaults. In file system there is many +different implementation for this problem. + +Some file systems show uid/gid when they are different than root, some +when user has set them and some show them always. There is also problem +that what if root uid/gid change. This code just choose to show them +always. This way we do not need to think this any more. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/ntfs_fs.h | 23 ++++++++++------------- + src/super.c | 12 ++++-------- + 2 files changed, 14 insertions(+), 21 deletions(-) + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 15bab48bc1ad13a9bc93d365ce92c8978635ec2b..372cda697dd4c587a82ed6381a1dd512b700d5c1 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -60,19 +60,16 @@ struct ntfs_mount_options { + u16 fs_fmask_inv; + u16 fs_dmask_inv; + +- unsigned uid : 1, /* uid was set. */ +- gid : 1, /* gid was set. */ +- fmask : 1, /* fmask was set. */ +- dmask : 1, /* dmask was set. */ +- sys_immutable : 1, /* Immutable system files. */ +- discard : 1, /* Issue discard requests on deletions. */ +- sparse : 1, /* Create sparse files. */ +- showmeta : 1, /* Show meta files. */ +- nohidden : 1, /* Do not show hidden files. */ +- force : 1, /* Rw mount dirty volume. */ +- noacsrules : 1, /*Exclude acs rules. */ +- prealloc : 1 /* Preallocate space when file is growing. */ +- ; ++ unsigned fmask : 1; /* fmask was set. */ ++ unsigned dmask : 1; /*dmask was set. */ ++ unsigned sys_immutable : 1; /* Immutable system files. */ ++ unsigned discard : 1; /* Issue discard requests on deletions. */ ++ unsigned sparse : 1; /* Create sparse files. */ ++ unsigned showmeta : 1; /* Show meta files. */ ++ unsigned nohidden : 1; /* Do not show hidden files. */ ++ unsigned force : 1; /* RW mount dirty volume. */ ++ unsigned noacsrules : 1; /* Exclude acs rules. */ ++ unsigned prealloc : 1; /* Preallocate space when file is growing. */ + }; + + /* Special value to unpack and deallocate. */ +diff --git a/src/super.c b/src/super.c +index 0690e7e4f00de4e6c89cacebdb4862bbb8f00bc6..3cba0b5e7ac72c444a55de38b791faa87732049f 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -294,13 +294,11 @@ static int ntfs_fs_parse_param(struct fs_context *fc, + opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(opts->fs_uid)) + return invalf(fc, "ntfs3: Invalid value for uid."); +- opts->uid = 1; + break; + case Opt_gid: + opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(opts->fs_gid)) + return invalf(fc, "ntfs3: Invalid value for gid."); +- opts->gid = 1; + break; + case Opt_umask: + if (result.uint_32 & ~07777) +@@ -521,12 +519,10 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + struct ntfs_mount_options *opts = sbi->options; + struct user_namespace *user_ns = seq_user_ns(m); + +- if (opts->uid) +- seq_printf(m, ",uid=%u", +- from_kuid_munged(user_ns, opts->fs_uid)); +- if (opts->gid) +- seq_printf(m, ",gid=%u", +- from_kgid_munged(user_ns, opts->fs_gid)); ++ seq_printf(m, ",uid=%u", ++ from_kuid_munged(user_ns, opts->fs_uid)); ++ seq_printf(m, ",gid=%u", ++ from_kgid_munged(user_ns, opts->fs_gid)); + if (opts->fmask) + seq_printf(m, ",fmask=%04o", ~opts->fs_fmask_inv); + if (opts->dmask) +-- +2.31.1 + diff --git a/SOURCES/0043-fs-ntfs3-Remove-redundant-initialization-of-variable.patch b/SOURCES/0043-fs-ntfs3-Remove-redundant-initialization-of-variable.patch new file mode 100644 index 0000000..7829886 --- /dev/null +++ b/SOURCES/0043-fs-ntfs3-Remove-redundant-initialization-of-variable.patch @@ -0,0 +1,33 @@ +From 0327c6d01a97a3242cf10717819994aa6e095a1d Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Fri, 3 Sep 2021 14:24:58 +0100 +Subject: [Backport 0327c6d01a97] src: Remove redundant initialization of + variable err + +The variable err is being initialized with a value that is never read, it +is being updated later on. The assignment is redundant and can be removed. + +Addresses-Coverity: ("Unused value") +Signed-off-by: Colin Ian King +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/index.c b/src/index.c +index 0daca9adc54c79084743efc2487b49a0ac0d3227..b1175542d854c44550858ce7620df68b99ee7b86 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -1401,7 +1401,7 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni, + static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, + CLST *vbn) + { +- int err = -ENOMEM; ++ int err; + struct ntfs_sb_info *sbi = ni->mi.sbi; + struct ATTRIB *bitmap; + struct ATTRIB *alloc; +-- +2.31.1 + diff --git a/SOURCES/0044-fs-ntfs3.-Add-forward-declarations-for-structs-to-de.patch b/SOURCES/0044-fs-ntfs3.-Add-forward-declarations-for-structs-to-de.patch new file mode 100644 index 0000000..25de8fa --- /dev/null +++ b/SOURCES/0044-fs-ntfs3.-Add-forward-declarations-for-structs-to-de.patch @@ -0,0 +1,32 @@ +From cde81f13ef63d47c3f0742fbe6f17a5123cf6ca4 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:21 +0300 +Subject: [Backport cde81f13ef63] src. Add forward declarations for + structs to debug.h + +Add forward declarations for structs so that we can include this file +without warnings even without linux/fs.h + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/debug.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/debug.h b/src/debug.h +index 31120569a87b9b07cf91f8e0b8c7291db059f4f3..53ef7489c75fd70f239d4e50af9a5e2f097c6bde 100644 +--- a/src/debug.h ++++ b/src/debug.h +@@ -11,6 +11,9 @@ + #ifndef _LINUX_NTFS3_DEBUG_H + #define _LINUX_NTFS3_DEBUG_H + ++struct super_block; ++struct inode; ++ + #ifndef Add2Ptr + #define Add2Ptr(P, I) ((void *)((u8 *)(P) + (I))) + #define PtrOffset(B, O) ((size_t)((size_t)(O) - (size_t)(B))) +-- +2.31.1 + diff --git a/SOURCES/0045-fs-ntfs3-Add-missing-header-files-to-ntfs.h.patch b/SOURCES/0045-fs-ntfs3-Add-missing-header-files-to-ntfs.h.patch new file mode 100644 index 0000000..dc8d307 --- /dev/null +++ b/SOURCES/0045-fs-ntfs3-Add-missing-header-files-to-ntfs.h.patch @@ -0,0 +1,47 @@ +From 4dfe83320e1e9665b986840b426742ea764e08d7 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:22 +0300 +Subject: [Backport 4dfe83320e1e] src: Add missing header files to ntfs.h + +We do not have header files at all in this file. Add following headers +and there is also explanation which for it was added. Note that +explanation might not be complete, but it just proofs it is needed. + + // SECTOR_SHIFT + // static_assert() + // cpu_to_le64, cpu_to_le32, ALIGN + // offsetof() + // memcmp() + //__le32, __le16 + +"debug.h" // PtrOffset(), Add2Ptr() + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/ntfs.h | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/ntfs.h b/src/ntfs.h +index 6bb3e595263b67c7cc66a0fecc5dfa68310826d4..695b684bce20dd4f274884ba9a1022555b47823a 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -10,6 +10,15 @@ + #ifndef _LINUX_NTFS3_NTFS_H + #define _LINUX_NTFS3_NTFS_H + ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++ + /* TODO: Check 4K MFT record and 512 bytes cluster. */ + + /* Activate this define to use binary search in indexes. */ +-- +2.31.1 + diff --git a/SOURCES/0046-fs-ntfs3-Add-missing-headers-and-forward-declaration.patch b/SOURCES/0046-fs-ntfs3-Add-missing-headers-and-forward-declaration.patch new file mode 100644 index 0000000..6b16fad --- /dev/null +++ b/SOURCES/0046-fs-ntfs3-Add-missing-headers-and-forward-declaration.patch @@ -0,0 +1,95 @@ +From f239b3a95dd4f7daba26ea17f339a5b19a7d40a1 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:23 +0300 +Subject: [Backport f239b3a95dd4] src: Add missing headers and forward + declarations to ntfs_fs.h + +We do not have headers at all in this file. We should have them so that +not every .c file needs to include all of the stuff which this file need +for building. This way we can remove some headers from other files and +get better picture what is needed. This can save some compilation time. +And this can help if we sometimes want to separate this one big header. + +Also use forward declarations for structs and enums when it not included +straight with include and it is used in function declarations input. +This will prevent possible compiler warning: + xxx declared inside parameter list will not be visible + outside of this definition or declaration + +Here is list which I made when parsing this. There is not necessarily +all example from this header file, but this just proofs we need it. + + SECTOR_SHIFT + sb_bread(), put_bh + put_page() + struct inode (Just struct ntfs_inode need it) + kunmap(), kmap() + cpu_to_leXX() ALIGN + kvfree() + struct mutex, mutex_(un/try)lock() + PageError() + read_mapping_page() + struct rb_root + struct rw_semaphore + krfree(), kzalloc() + memset() + struct timespec64 + uXX, __leXX + kuid_t, kgid_t + do_div() + PAGE_SIZE + +"debug.h" ntfs_err() (Just one entry. Maybe we can drop this) +"ntfs.h" Do you even ask? + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/ntfs_fs.h | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 372cda697dd4c587a82ed6381a1dd512b700d5c1..dae6dd4ac6198fe735749b8e1405e110da4ddd62 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -9,6 +9,37 @@ + #ifndef _LINUX_NTFS3_NTFS_FS_H + #define _LINUX_NTFS3_NTFS_FS_H + ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "ntfs.h" ++ ++struct dentry; ++struct fiemap_extent_info; ++struct user_namespace; ++struct page; ++struct writeback_control; ++enum utf16_endian; ++ ++ + #define MINUS_ONE_T ((size_t)(-1)) + /* Biggest MFT / smallest cluster */ + #define MAXIMUM_BYTES_PER_MFT 4096 +-- +2.31.1 + diff --git a/SOURCES/0047-fs-ntfs3-Add-missing-header-and-guards-to-lib-header.patch b/SOURCES/0047-fs-ntfs3-Add-missing-header-and-guards-to-lib-header.patch new file mode 100644 index 0000000..6ef2e53 --- /dev/null +++ b/SOURCES/0047-fs-ntfs3-Add-missing-header-and-guards-to-lib-header.patch @@ -0,0 +1,60 @@ +From b6ba81034b1b74cf426abcece4becda2611504a4 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:24 +0300 +Subject: [Backport b6ba81034b1b] src: Add missing header and guards to + lib/ headers + +size_t needs header. Add missing header guards so that compiler will +only include these ones. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/lib/decompress_common.h | 5 +++++ + src/lib/lib.h | 6 ++++++ + 2 files changed, 11 insertions(+) + +diff --git a/src/lib/decompress_common.h b/src/lib/decompress_common.h +index 2d70ae42f1b511f3b43af7a9b886ac1ffd21822b..dd7ced000d0e755ca1b337273dab7577c194a7c4 100644 +--- a/src/lib/decompress_common.h ++++ b/src/lib/decompress_common.h +@@ -5,6 +5,9 @@ + * Copyright (C) 2015 Eric Biggers + */ + ++#ifndef _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H ++#define _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H ++ + #include + #include + #include +@@ -336,3 +339,5 @@ static forceinline u8 *lz_copy(u8 *dst, u32 length, u32 offset, const u8 *bufend + + return dst; + } ++ ++#endif /* _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H */ +diff --git a/src/lib/lib.h b/src/lib/lib.h +index f508fbad2e712d946274b13f0ec7b244dc264a4d..90309a5ae59c7a98f3f0b61f5883fe390790eb50 100644 +--- a/src/lib/lib.h ++++ b/src/lib/lib.h +@@ -7,6 +7,10 @@ + * - linux kernel code style + */ + ++#ifndef _LINUX_NTFS3_LIB_LIB_H ++#define _LINUX_NTFS3_LIB_LIB_H ++ ++#include + + /* globals from xpress_decompress.c */ + struct xpress_decompressor *xpress_allocate_decompressor(void); +@@ -24,3 +28,5 @@ int lzx_decompress(struct lzx_decompressor *__restrict d, + const void *__restrict compressed_data, + size_t compressed_size, void *__restrict uncompressed_data, + size_t uncompressed_size); ++ ++#endif /* _LINUX_NTFS3_LIB_LIB_H */ +-- +2.31.1 + diff --git a/SOURCES/0048-fs-ntfs3-Change-right-headers-to-bitfunc.c.patch b/SOURCES/0048-fs-ntfs3-Change-right-headers-to-bitfunc.c.patch new file mode 100644 index 0000000..a703e45 --- /dev/null +++ b/SOURCES/0048-fs-ntfs3-Change-right-headers-to-bitfunc.c.patch @@ -0,0 +1,36 @@ +From c632f639d1d9bb3e379741a30c6882342d859daf Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:25 +0300 +Subject: [Backport c632f639d1d9] src: Change right headers to bitfunc.c + +We only need linux/types.h for types like u8 etc. So we can remove rest +and help compiler a little bit. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/bitfunc.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +diff --git a/src/bitfunc.c b/src/bitfunc.c +index ce304d40b5e1642cc84d48143e2aa961ac3ea735..bf10e2da5c6e3d29d3788055b2b664927cc9f29b 100644 +--- a/src/bitfunc.c ++++ b/src/bitfunc.c +@@ -5,13 +5,8 @@ + * + */ + +-#include +-#include +-#include +-#include ++#include + +-#include "debug.h" +-#include "ntfs.h" + #include "ntfs_fs.h" + + #define BITS_IN_SIZE_T (sizeof(size_t) * 8) +-- +2.31.1 + diff --git a/SOURCES/0049-fs-ntfs3-Change-right-headers-to-upcase.c.patch b/SOURCES/0049-fs-ntfs3-Change-right-headers-to-upcase.c.patch new file mode 100644 index 0000000..3b225fc --- /dev/null +++ b/SOURCES/0049-fs-ntfs3-Change-right-headers-to-upcase.c.patch @@ -0,0 +1,41 @@ +From f97676611937f4550a60970acadeccbd5e6f124c Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:26 +0300 +Subject: [Backport f97676611937] src: Change right headers to upcase.c + +There is no headers. They will be included through ntfs_fs.c, but that +is not right thing to do. Let's include headers what this file need +straight away. + +types.h is needed for __le16, u8 etc. +kernel.h is needed for le16_to_cpu() + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/upcase.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/src/upcase.c b/src/upcase.c +index bbeba778237eefc318b00e04b05ad0ac1592e1ec..b5e8256fd710d5263b2be07b92df14cb8eea3c35 100644 +--- a/src/upcase.c ++++ b/src/upcase.c +@@ -5,13 +5,9 @@ + * + */ + +-#include +-#include +-#include +-#include ++#include ++#include + +-#include "debug.h" +-#include "ntfs.h" + #include "ntfs_fs.h" + + static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr) +-- +2.31.1 + diff --git a/SOURCES/0050-fs-ntfs3-Change-right-headers-to-lznt.c.patch b/SOURCES/0050-fs-ntfs3-Change-right-headers-to-lznt.c.patch new file mode 100644 index 0000000..8c10d85 --- /dev/null +++ b/SOURCES/0050-fs-ntfs3-Change-right-headers-to-lznt.c.patch @@ -0,0 +1,49 @@ +From 977d0558e310113767feed91fdcc618691dd2835 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:27 +0300 +Subject: [Backport 977d0558e310] src: Change right headers to lznt.c + +There is lot of headers which we do not need in this file. Delete them +and add what we really need. Here is list which identify why we need +this header. + + // min() + // kzalloc() + // offsetof() + // memcpy(), memset() + // u8, size_t, etc. + +"debug.h" // PtrOffset() + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/lznt.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/lznt.c b/src/lznt.c +index f1f691a67cc490254957f062cfc38ec12ac7e834..12ba42518efea5a6f3198499b75b3023d0032be5 100644 +--- a/src/lznt.c ++++ b/src/lznt.c +@@ -5,13 +5,13 @@ + * + */ + +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++#include ++#include + + #include "debug.h" +-#include "ntfs.h" + #include "ntfs_fs.h" + + // clang-format off +-- +2.31.1 + diff --git a/SOURCES/0051-fs-ntfs3-Remove-unneeded-header-files-from-c-files.patch b/SOURCES/0051-fs-ntfs3-Remove-unneeded-header-files-from-c-files.patch new file mode 100644 index 0000000..60dd06b --- /dev/null +++ b/SOURCES/0051-fs-ntfs3-Remove-unneeded-header-files-from-c-files.patch @@ -0,0 +1,260 @@ +From 9c2aadd0fdf88a7ebeebd1e97a9c66b07ad3e14a Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 19:15:28 +0300 +Subject: [Backport 9c2aadd0fdf8] src: Remove unneeded header files from c + files + +We have lot of unnecessary headers in these files. Remove them so that +we help compiler a little bit. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 5 ----- + src/attrlist.c | 3 --- + src/bitmap.c | 3 --- + src/dir.c | 3 --- + src/file.c | 1 - + src/frecord.c | 3 --- + src/fslog.c | 4 ---- + src/fsntfs.c | 1 - + src/index.c | 1 - + src/inode.c | 2 -- + src/namei.c | 4 ---- + src/record.c | 3 --- + src/run.c | 2 -- + src/super.c | 2 -- + src/xattr.c | 3 --- + 15 files changed, 40 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index b1055b284c60323168be99ce69b18e7dbd7bb969..12cff28f3e7155231e56864792dd764487297f37 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -6,12 +6,7 @@ + * TODO: Merge attr_set_size/attr_data_get_block/attr_allocate_frame? + */ + +-#include +-#include + #include +-#include +-#include +-#include + #include + + #include "debug.h" +diff --git a/src/attrlist.c b/src/attrlist.c +index fa32399eb5171ce25f037fbd1bd8e483a37b839c..b9da527b96aa2ab00bf43d439d8527b587946a9d 100644 +--- a/src/attrlist.c ++++ b/src/attrlist.c +@@ -5,10 +5,7 @@ + * + */ + +-#include +-#include + #include +-#include + + #include "debug.h" + #include "ntfs.h" +diff --git a/src/bitmap.c b/src/bitmap.c +index 831501555009533d8b3eac04b6258e7072bd6304..a03584674fea13c818f341f8f2a5147957507709 100644 +--- a/src/bitmap.c ++++ b/src/bitmap.c +@@ -10,12 +10,9 @@ + * + */ + +-#include + #include + #include +-#include + +-#include "debug.h" + #include "ntfs.h" + #include "ntfs_fs.h" + +diff --git a/src/dir.c b/src/dir.c +index 40440df021eff2a95a87ff7a2af38345a7c40793..785e72d4392e152ef6833721e887ed9ef056094b 100644 +--- a/src/dir.c ++++ b/src/dir.c +@@ -7,10 +7,7 @@ + * + */ + +-#include +-#include + #include +-#include + #include + + #include "debug.h" +diff --git a/src/file.c b/src/file.c +index 0743d806c567409dd2845b6577f2ae3adf92b383..5fb3508e5422cc6654381897d6fdb4a204a543a4 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -12,7 +12,6 @@ + #include + #include + #include +-#include + + #include "debug.h" + #include "ntfs.h" +diff --git a/src/frecord.c b/src/frecord.c +index 938b12d56ca676308d2af82666a4f6f19cc8f122..080264ced9090a61825dbe00d96458c76538468b 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -5,11 +5,8 @@ + * + */ + +-#include +-#include + #include + #include +-#include + #include + + #include "debug.h" +diff --git a/src/fslog.c b/src/fslog.c +index b5853aed0e25bc6d93b3f6f667ae36feda3d980b..6e7f9b72792b354a6e588b506c7352b286ed58bd 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -6,12 +6,8 @@ + */ + + #include +-#include + #include +-#include +-#include + #include +-#include + #include + + #include "debug.h" +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 91e3743e1442f10cfc727eba0587a9ef0b20c039..9232a7f410c68bb0b1f3a5cedd2295b814372404 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -8,7 +8,6 @@ + #include + #include + #include +-#include + + #include "debug.h" + #include "ntfs.h" +diff --git a/src/index.c b/src/index.c +index b1175542d854c44550858ce7620df68b99ee7b86..35b77c92e96dc661baac0cc1ecaa3d0f130ef8e6 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -8,7 +8,6 @@ + #include + #include + #include +-#include + + #include "debug.h" + #include "ntfs.h" +diff --git a/src/inode.c b/src/inode.c +index 9f740fd301b24f2b1a67da0b78d091b3a3ae4705..33f278a0947ca23e103683b9daece8f539da355b 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -5,10 +5,8 @@ + * + */ + +-#include + #include + #include +-#include + #include + #include + #include +diff --git a/src/namei.c b/src/namei.c +index e58415d0713280297df1b7bac30eb17642431fa0..1c475da4e19d1158e3c11fcb749fb0825d2d7ae6 100644 +--- a/src/namei.c ++++ b/src/namei.c +@@ -5,11 +5,7 @@ + * + */ + +-#include +-#include + #include +-#include +-#include + #include + + #include "debug.h" +diff --git a/src/record.c b/src/record.c +index 103705c86772f45a90227e6434449e99c4d7d94b..861e35791506e801dc446414d935d5463e64a1fb 100644 +--- a/src/record.c ++++ b/src/record.c +@@ -5,10 +5,7 @@ + * + */ + +-#include +-#include + #include +-#include + + #include "debug.h" + #include "ntfs.h" +diff --git a/src/run.c b/src/run.c +index 26ed2b64345e665a39c6b37d88b29c5b81592ecd..a8fec651f9732878ad871cc476d8a6fcf28f9312 100644 +--- a/src/run.c ++++ b/src/run.c +@@ -7,10 +7,8 @@ + */ + + #include +-#include + #include + #include +-#include + + #include "debug.h" + #include "ntfs.h" +diff --git a/src/super.c b/src/super.c +index 3cba0b5e7ac72c444a55de38b791faa87732049f..acfa00acf4dd1c94ebfc63a47270a86570e0073e 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -23,14 +23,12 @@ + * + */ + +-#include + #include + #include + #include + #include + #include + #include +-#include + #include + #include + #include +diff --git a/src/xattr.c b/src/xattr.c +index 6f88cb77a17f666fc7cdcdcdcd44b032ceca1c07..a8c5a899f0dd308fe5014bf3b384bc903ca0bf68 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -5,10 +5,7 @@ + * + */ + +-#include +-#include + #include +-#include + #include + #include + #include +-- +2.31.1 + diff --git a/SOURCES/0052-fs-ntfs3-Limit-binary-search-table-size.patch b/SOURCES/0052-fs-ntfs3-Limit-binary-search-table-size.patch new file mode 100644 index 0000000..4371f73 --- /dev/null +++ b/SOURCES/0052-fs-ntfs3-Limit-binary-search-table-size.patch @@ -0,0 +1,167 @@ +From 162333efa8dc4984d2ca0a2eb85528e13366f271 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 18:40:48 +0300 +Subject: [Backport 162333efa8dc] src: Limit binary search table size + +Current binary search allocates memory for table and fill whole table +before we start actual binary search. This is quite inefficient because +table fill will always be O(n). Also if table is huge we need to +reallocate memory which is costly. + +This implementation use just stack memory and always when table is full +we will check if last element is <= and if not start table fill again. +The idea was that it would be same cost as table reallocation. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 110 ++++++++++++++++++----------------------------- + 1 file changed, 41 insertions(+), 69 deletions(-) + +diff --git a/src/index.c b/src/index.c +index 35b77c92e96dc661baac0cc1ecaa3d0f130ef8e6..a16256ab3e9ffd5bcc660c602194c9bd2de7cfc9 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -677,98 +677,70 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + u32 off = le32_to_cpu(hdr->de_off); + + #ifdef NTFS3_INDEX_BINARY_SEARCH +- int max_idx = 0, fnd, min_idx; +- int nslots = 64; +- u16 *offs; ++ struct NTFS_DE *found = NULL; ++ int min_idx = 0, mid_idx, max_idx = 0; ++ int diff2; ++ u16 offs[64]; + + if (end > 0x10000) + goto next; + +- offs = kmalloc(sizeof(u16) * nslots, GFP_NOFS); +- if (!offs) +- goto next; ++fill_table: ++ if (off + sizeof(struct NTFS_DE) > end) ++ return NULL; + +- /* Use binary search algorithm. */ +-next1: +- if (off + sizeof(struct NTFS_DE) > end) { +- e = NULL; +- goto out1; +- } + e = Add2Ptr(hdr, off); + e_size = le16_to_cpu(e->size); + +- if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) { +- e = NULL; +- goto out1; +- } +- +- if (max_idx >= nslots) { +- u16 *ptr; +- int new_slots = ALIGN(2 * nslots, 8); +- +- ptr = kmalloc(sizeof(u16) * new_slots, GFP_NOFS); +- if (ptr) +- memcpy(ptr, offs, sizeof(u16) * max_idx); +- kfree(offs); +- offs = ptr; +- nslots = new_slots; +- if (!ptr) +- goto next; +- } +- +- /* Store entry table. */ +- offs[max_idx] = off; ++ if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) ++ return NULL; + + if (!de_is_last(e)) { ++ offs[max_idx] = off; + off += e_size; +- max_idx += 1; +- goto next1; +- } + +- /* +- * Table of pointers is created. +- * Use binary search to find entry that is <= to the search value. +- */ +- fnd = -1; +- min_idx = 0; ++ max_idx++; ++ if (max_idx < ARRAY_SIZE(offs)) ++ goto fill_table; + +- while (min_idx <= max_idx) { +- int mid_idx = min_idx + ((max_idx - min_idx) >> 1); +- int diff2; +- +- e = Add2Ptr(hdr, offs[mid_idx]); ++ max_idx--; ++ } + +- e_key_len = le16_to_cpu(e->key_size); ++binary_search: ++ e_key_len = le16_to_cpu(e->key_size); + +- diff2 = (*cmp)(key, key_len, e + 1, e_key_len, ctx); ++ diff2 = (*cmp)(key, key_len, e + 1, e_key_len, ctx); ++ if (diff2 > 0) { ++ if (found) { ++ min_idx = mid_idx + 1; ++ } else { ++ if (de_is_last(e)) ++ return NULL; + +- if (!diff2) { +- *diff = 0; +- goto out1; ++ max_idx = 0; ++ goto fill_table; + } +- +- if (diff2 < 0) { ++ } else if (diff2 < 0) { ++ if (found) + max_idx = mid_idx - 1; +- fnd = mid_idx; +- if (!fnd) +- break; +- } else { +- min_idx = mid_idx + 1; +- } +- } ++ else ++ max_idx--; + +- if (fnd == -1) { +- e = NULL; +- goto out1; ++ found = e; ++ } else { ++ *diff = 0; ++ return e; + } + +- *diff = -1; +- e = Add2Ptr(hdr, offs[fnd]); ++ if (min_idx > max_idx) { ++ *diff = -1; ++ return found; ++ } + +-out1: +- kfree(offs); ++ mid_idx = (min_idx + max_idx) >> 1; ++ e = Add2Ptr(hdr, offs[mid_idx]); + +- return e; ++ goto binary_search; + #endif + + next: +-- +2.31.1 + diff --git a/SOURCES/0053-fs-ntfs3-Make-binary-search-to-search-smaller-chunks.patch b/SOURCES/0053-fs-ntfs3-Make-binary-search-to-search-smaller-chunks.patch new file mode 100644 index 0000000..c2eb1d8 --- /dev/null +++ b/SOURCES/0053-fs-ntfs3-Make-binary-search-to-search-smaller-chunks.patch @@ -0,0 +1,60 @@ +From ef9297007e9904588682699e618c56401f61d1c2 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 18:40:49 +0300 +Subject: [Backport ef9297007e99] src: Make binary search to search + smaller chunks in beginning + +We could try to optimize algorithm to first fill just small table and +after that use bigger table all the way up to ARRAY_SIZE(offs). This +way we can use bigger search array, but not lose benefits with entry +count smaller < ARRAY_SIZE(offs). + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/index.c b/src/index.c +index a16256ab3e9ffd5bcc660c602194c9bd2de7cfc9..3ad1ee608e531ea7c7793e691fe706ee0a6c33a4 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + #include "debug.h" + #include "ntfs.h" +@@ -679,8 +680,9 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + #ifdef NTFS3_INDEX_BINARY_SEARCH + struct NTFS_DE *found = NULL; + int min_idx = 0, mid_idx, max_idx = 0; ++ int table_size = 8; + int diff2; +- u16 offs[64]; ++ u16 offs[128]; + + if (end > 0x10000) + goto next; +@@ -700,7 +702,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + off += e_size; + + max_idx++; +- if (max_idx < ARRAY_SIZE(offs)) ++ if (max_idx < table_size) + goto fill_table; + + max_idx--; +@@ -718,6 +720,7 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + return NULL; + + max_idx = 0; ++ table_size = min(table_size * 2, 128); + goto fill_table; + } + } else if (diff2 < 0) { +-- +2.31.1 + diff --git a/SOURCES/0054-fs-ntfs3-Always-use-binary-search-with-entry-search.patch b/SOURCES/0054-fs-ntfs3-Always-use-binary-search-with-entry-search.patch new file mode 100644 index 0000000..2738927 --- /dev/null +++ b/SOURCES/0054-fs-ntfs3-Always-use-binary-search-with-entry-search.patch @@ -0,0 +1,116 @@ +From 8e69212253d320d4768071086b1111e6ab91d9bd Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 2 Sep 2021 18:40:50 +0300 +Subject: [Backport 8e69212253d3] src: Always use binary search with entry + search + +We do not have any reason to keep old linear search in. Before this was +used for error path or if table was so big that it cannot be allocated. +Current binary search implementation won't need error path. Remove old +references to linear entry search. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 50 ++++++------------------------------------------ + src/ntfs.h | 3 --- + 2 files changed, 6 insertions(+), 47 deletions(-) + +diff --git a/src/index.c b/src/index.c +index 3ad1ee608e531ea7c7793e691fe706ee0a6c33a4..4f71a91f07d9fc56efe6a80ebf52995e2e9a65de 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -671,22 +671,16 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + const struct INDEX_HDR *hdr, const void *key, + size_t key_len, const void *ctx, int *diff) + { +- struct NTFS_DE *e; ++ struct NTFS_DE *e, *found = NULL; + NTFS_CMP_FUNC cmp = indx->cmp; ++ int min_idx = 0, mid_idx, max_idx = 0; ++ int diff2; ++ int table_size = 8; + u32 e_size, e_key_len; + u32 end = le32_to_cpu(hdr->used); + u32 off = le32_to_cpu(hdr->de_off); +- +-#ifdef NTFS3_INDEX_BINARY_SEARCH +- struct NTFS_DE *found = NULL; +- int min_idx = 0, mid_idx, max_idx = 0; +- int table_size = 8; +- int diff2; + u16 offs[128]; + +- if (end > 0x10000) +- goto next; +- + fill_table: + if (off + sizeof(struct NTFS_DE) > end) + return NULL; +@@ -720,7 +714,8 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + return NULL; + + max_idx = 0; +- table_size = min(table_size * 2, 128); ++ table_size = min(table_size * 2, ++ (int)ARRAY_SIZE(offs)); + goto fill_table; + } + } else if (diff2 < 0) { +@@ -744,39 +739,6 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx, + e = Add2Ptr(hdr, offs[mid_idx]); + + goto binary_search; +-#endif +- +-next: +- /* +- * Entries index are sorted. +- * Enumerate all entries until we find entry +- * that is <= to the search value. +- */ +- if (off + sizeof(struct NTFS_DE) > end) +- return NULL; +- +- e = Add2Ptr(hdr, off); +- e_size = le16_to_cpu(e->size); +- +- if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) +- return NULL; +- +- off += e_size; +- +- e_key_len = le16_to_cpu(e->key_size); +- +- *diff = (*cmp)(key, key_len, e + 1, e_key_len, ctx); +- if (!*diff) +- return e; +- +- if (*diff <= 0) +- return e; +- +- if (de_is_last(e)) { +- *diff = 1; +- return e; +- } +- goto next; + } + + /* +diff --git a/src/ntfs.h b/src/ntfs.h +index 695b684bce20dd4f274884ba9a1022555b47823a..303a162c31587475adb8c3190b16fa451e9616a7 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -21,9 +21,6 @@ + + /* TODO: Check 4K MFT record and 512 bytes cluster. */ + +-/* Activate this define to use binary search in indexes. */ +-#define NTFS3_INDEX_BINARY_SEARCH +- + /* Check each run for marked clusters. */ + #define NTFS3_CHECK_FREE_CLST + +-- +2.31.1 + diff --git a/SOURCES/0055-fs-ntfs3-Remove-before-constant-in-ni_insert_residen.patch b/SOURCES/0055-fs-ntfs3-Remove-before-constant-in-ni_insert_residen.patch new file mode 100644 index 0000000..dbd9df1 --- /dev/null +++ b/SOURCES/0055-fs-ntfs3-Remove-before-constant-in-ni_insert_residen.patch @@ -0,0 +1,30 @@ +From 7d95995ab4de07ee642c925aa6dbf6d07069a751 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 11:34:38 +0300 +Subject: [Backport 7d95995ab4de] src: Remove '+' before constant in + ni_insert_resident() + +No need for plus sign here. So remove it. Checkpatch will also be happy. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/frecord.c b/src/frecord.c +index 080264ced9090a61825dbe00d96458c76538468b..df41eae8f7e1bf94800e6cb2b85de28ef992d6ee 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -1448,7 +1448,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, + attr->res.flags = RESIDENT_FLAG_INDEXED; + + /* is_attr_indexed(attr)) == true */ +- le16_add_cpu(&ni->mi.mrec->hard_links, +1); ++ le16_add_cpu(&ni->mi.mrec->hard_links, 1); + ni->mi.dirty = true; + } + attr->res.res = 0; +-- +2.31.1 + diff --git a/SOURCES/0056-fs-ntfs3-Place-Comparisons-constant-right-side-of-th.patch b/SOURCES/0056-fs-ntfs3-Place-Comparisons-constant-right-side-of-th.patch new file mode 100644 index 0000000..205215f --- /dev/null +++ b/SOURCES/0056-fs-ntfs3-Place-Comparisons-constant-right-side-of-th.patch @@ -0,0 +1,31 @@ +From 4ca7fe57f21a25afc4a651db5145bfe090c6248f Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 11:34:39 +0300 +Subject: [Backport 4ca7fe57f21a] src: Place Comparisons constant right + side of the test + +For better code readability place constant always right side of the +test. This will also address checkpatch warning. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/frecord.c b/src/frecord.c +index df41eae8f7e1bf94800e6cb2b85de28ef992d6ee..2f8af53da219c3486f2d5eef5d2f5a136c126b9e 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -1603,7 +1603,7 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, + + *le = NULL; + +- if (FILE_NAME_POSIX == name_type) ++ if (name_type == FILE_NAME_POSIX) + return NULL; + + /* Enumerate all names. */ +-- +2.31.1 + diff --git a/SOURCES/0057-fs-ntfs3-Remove-braces-from-single-statment-block.patch b/SOURCES/0057-fs-ntfs3-Remove-braces-from-single-statment-block.patch new file mode 100644 index 0000000..1fe1416 --- /dev/null +++ b/SOURCES/0057-fs-ntfs3-Remove-braces-from-single-statment-block.patch @@ -0,0 +1,34 @@ +From 2829e39e0e8add377508b3c6ef4cf48e6db324fb Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 11:34:40 +0300 +Subject: [Backport 2829e39e0e8a] src: Remove braces from single statment + block + +Remove braces from single statment block as they are not needed. Also +Linux kernel coding style guide recommend this and checkpatch warn about +this. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/frecord.c b/src/frecord.c +index 2f8af53da219c3486f2d5eef5d2f5a136c126b9e..1e1a09034f65b023872d86b866ee5b3907634506 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -2903,9 +2903,8 @@ bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni, + memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de + 1, de_key_size); + mi_get_ref(&ni->mi, &de->ref); + +- if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1)) { ++ if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1)) + return false; +- } + } + + return true; +-- +2.31.1 + diff --git a/SOURCES/0058-fs-ntfs3-Remove-tabs-before-spaces-from-comment.patch b/SOURCES/0058-fs-ntfs3-Remove-tabs-before-spaces-from-comment.patch new file mode 100644 index 0000000..b6e37e0 --- /dev/null +++ b/SOURCES/0058-fs-ntfs3-Remove-tabs-before-spaces-from-comment.patch @@ -0,0 +1,31 @@ +From cffb5152eea82de890dd418a90612aede96068d6 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 11:34:41 +0300 +Subject: [Backport cffb5152eea8] src: Remove tabs before spaces from + comment + +Remove tabs before spaces from comment as recommended by kernel coding +style. Checkpatch also warn about these. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/lznt.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lznt.c b/src/lznt.c +index 12ba42518efea5a6f3198499b75b3023d0032be5..28f654561f279a5d09ddd4a1352930b0fefb5416 100644 +--- a/src/lznt.c ++++ b/src/lznt.c +@@ -292,7 +292,7 @@ static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr, + /* + * get_lznt_ctx + * @level: 0 - Standard compression. +- * !0 - Best compression, requires a lot of cpu. ++ * !0 - Best compression, requires a lot of cpu. + */ + struct lznt *get_lznt_ctx(int level) + { +-- +2.31.1 + diff --git a/SOURCES/0059-fs-ntfs3-Fix-ntfs_look_for_free_space-does-only-repo.patch b/SOURCES/0059-fs-ntfs3-Fix-ntfs_look_for_free_space-does-only-repo.patch new file mode 100644 index 0000000..291b1cd --- /dev/null +++ b/SOURCES/0059-fs-ntfs3-Fix-ntfs_look_for_free_space-does-only-repo.patch @@ -0,0 +1,124 @@ +From edb853ff3dc01c22577ea5d0383d067b68a6d663 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 17:28:39 +0300 +Subject: [Backport edb853ff3dc0] src: Fix ntfs_look_for_free_space() does + only report -ENOSPC + +If ntfs_refresh_zone() returns error it will be changed to -ENOSPC. It +is not right. Also caller of this functions also check other errors. + +Fixes: 78ab59fee07f ("src: Rework file operations") +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/fsntfs.c | 51 +++++++++++++++++++++++++---------------------- + 1 file changed, 27 insertions(+), 24 deletions(-) + +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 9232a7f410c68bb0b1f3a5cedd2295b814372404..e77fad89eaed372cbb972cab51d904fe99b3d565 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -357,7 +357,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + enum ALLOCATE_OPT opt) + { + int err; +- CLST alen = 0; ++ CLST alen; + struct super_block *sb = sbi->sb; + size_t alcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen; + struct wnd_bitmap *wnd = &sbi->used.bitmap; +@@ -369,13 +369,15 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + if (!zlen) { + err = ntfs_refresh_zone(sbi); + if (err) +- goto out; ++ goto up_write; ++ + zlen = wnd_zone_len(wnd); + } + + if (!zlen) { + ntfs_err(sbi->sb, "no free space to extend mft"); +- goto out; ++ err = -ENOSPC; ++ goto up_write; + } + + lcn = wnd_zone_bit(wnd); +@@ -384,12 +386,11 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + wnd_zone_set(wnd, lcn + alen, zlen - alen); + + err = wnd_set_used(wnd, lcn, alen); +- if (err) { +- up_write(&wnd->rw_lock); +- return err; +- } ++ if (err) ++ goto up_write; ++ + alcn = lcn; +- goto out; ++ goto space_found; + } + /* + * 'Cause cluster 0 is always used this value means that we should use +@@ -403,15 +404,17 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + + alen = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &alcn); + if (alen) +- goto out; ++ goto space_found; + + /* Try to use clusters from MftZone. */ + zlen = wnd_zone_len(wnd); + zeroes = wnd_zeroes(wnd); + + /* Check too big request */ +- if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE) +- goto out; ++ if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE) { ++ err = -ENOSPC; ++ goto up_write; ++ } + + /* How many clusters to cat from zone. */ + zlcn = wnd_zone_bit(wnd); +@@ -430,22 +433,22 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + /* Allocate continues clusters. */ + alen = wnd_find(wnd, len, 0, + BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn); +- +-out: +- if (alen) { +- err = 0; +- *new_len = alen; +- *new_lcn = alcn; +- +- ntfs_unmap_meta(sb, alcn, alen); +- +- /* Set hint for next requests. */ +- if (!(opt & ALLOCATE_MFT)) +- sbi->used.next_free_lcn = alcn + alen; +- } else { ++ if (!alen) { + err = -ENOSPC; ++ goto up_write; + } + ++space_found: ++ err = 0; ++ *new_len = alen; ++ *new_lcn = alcn; ++ ++ ntfs_unmap_meta(sb, alcn, alen); ++ ++ /* Set hint for next requests. */ ++ if (!(opt & ALLOCATE_MFT)) ++ sbi->used.next_free_lcn = alcn + alen; ++up_write: + up_write(&wnd->rw_lock); + return err; + } +-- +2.31.1 + diff --git a/SOURCES/0060-fs-ntfs3-Remove-always-false-condition-check.patch b/SOURCES/0060-fs-ntfs3-Remove-always-false-condition-check.patch new file mode 100644 index 0000000..37dd73a --- /dev/null +++ b/SOURCES/0060-fs-ntfs3-Remove-always-false-condition-check.patch @@ -0,0 +1,35 @@ +From f162f7b8dbc29742896f8a7e678bb31192716ae0 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 17:28:40 +0300 +Subject: [Backport f162f7b8dbc2] src: Remove always false condition check + +We do not need this check as this is same thing as +NTFS_MIN_MFT_ZONE > zlen. We already check NTFS_MIN_MFT_ZONE <= zlen and +exit because is too big request. Remove it so code is cleaner. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/fsntfs.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/fsntfs.c b/src/fsntfs.c +index e77fad89eaed372cbb972cab51d904fe99b3d565..4cd24e4e58ff26b3ddce5f891783a36cca6170a7 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -422,11 +422,8 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + ztrim = len > zlen ? zlen : (len > zlen2 ? len : zlen2); + new_zlen = zlen - ztrim; + +- if (new_zlen < NTFS_MIN_MFT_ZONE) { ++ if (new_zlen < NTFS_MIN_MFT_ZONE) + new_zlen = NTFS_MIN_MFT_ZONE; +- if (new_zlen > zlen) +- new_zlen = zlen; +- } + + wnd_zone_set(wnd, zlcn, new_zlen); + +-- +2.31.1 + diff --git a/SOURCES/0061-fs-ntfs3-Use-clamp-max-macros-instead-of-comparisons.patch b/SOURCES/0061-fs-ntfs3-Use-clamp-max-macros-instead-of-comparisons.patch new file mode 100644 index 0000000..24137a7 --- /dev/null +++ b/SOURCES/0061-fs-ntfs3-Use-clamp-max-macros-instead-of-comparisons.patch @@ -0,0 +1,45 @@ +From b5322eb1ae94572f5a52e804857e641b846059f4 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 17:28:41 +0300 +Subject: [Backport b5322eb1ae94] src: Use clamp/max macros instead of + comparisons + +We can make code little more readable by using kernel macros clamp/max. + +This were found with kernel included Coccinelle minmax script. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/fsntfs.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 4cd24e4e58ff26b3ddce5f891783a36cca6170a7..c964d3996aab1dff3923337acaa00e40869a70f5 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + #include "debug.h" + #include "ntfs.h" +@@ -419,11 +420,8 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + /* How many clusters to cat from zone. */ + zlcn = wnd_zone_bit(wnd); + zlen2 = zlen >> 1; +- ztrim = len > zlen ? zlen : (len > zlen2 ? len : zlen2); +- new_zlen = zlen - ztrim; +- +- if (new_zlen < NTFS_MIN_MFT_ZONE) +- new_zlen = NTFS_MIN_MFT_ZONE; ++ ztrim = clamp_val(len, zlen2, zlen); ++ new_zlen = max_t(size_t, zlen - ztrim, NTFS_MIN_MFT_ZONE); + + wnd_zone_set(wnd, zlcn, new_zlen); + +-- +2.31.1 + diff --git a/SOURCES/0062-fs-ntfs3-Use-min-max-macros-instated-of-ternary-oper.patch b/SOURCES/0062-fs-ntfs3-Use-min-max-macros-instated-of-ternary-oper.patch new file mode 100644 index 0000000..0ae5360 --- /dev/null +++ b/SOURCES/0062-fs-ntfs3-Use-min-max-macros-instated-of-ternary-oper.patch @@ -0,0 +1,130 @@ +From 6e3331ee34461be37f50912295a2e924a673dbc6 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 7 Sep 2021 17:28:42 +0300 +Subject: [Backport 6e3331ee3446] src: Use min/max macros instated of + ternary operators + +We can make code little bit more readable by using min/max macros. + +These were found with Coccinelle. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 3 ++- + src/bitmap.c | 11 ++++++----- + src/fsntfs.c | 6 +++--- + 3 files changed, 11 insertions(+), 9 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index 12cff28f3e7155231e56864792dd764487297f37..c15cc17e3cd92864f30fd0d3fbb8f20b98437cde 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + + #include "debug.h" + #include "ntfs.h" +@@ -1961,7 +1962,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) + return 0; + + from = vbo; +- to = (vbo + bytes) < data_size ? (vbo + bytes) : data_size; ++ to = min_t(u64, vbo + bytes, data_size); + memset(Add2Ptr(resident_data(attr_b), from), 0, to - from); + return 0; + } +diff --git a/src/bitmap.c b/src/bitmap.c +index a03584674fea13c818f341f8f2a5147957507709..aa184407520f0263844839e03d2f1edde3c6628f 100644 +--- a/src/bitmap.c ++++ b/src/bitmap.c +@@ -12,6 +12,7 @@ + + #include + #include ++#include + + #include "ntfs.h" + #include "ntfs_fs.h" +@@ -432,7 +433,7 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len) + ; + } else { + n3 = rb_next(&e->count.node); +- max_new_len = len > new_len ? len : new_len; ++ max_new_len = max(len, new_len); + if (!n3) { + wnd->extent_max = max_new_len; + } else { +@@ -728,7 +729,7 @@ int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits) + wbits = wnd->bits_last; + + tail = wbits - wbit; +- op = tail < bits ? tail : bits; ++ op = min_t(u32, tail, bits); + + bh = wnd_map(wnd, iw); + if (IS_ERR(bh)) { +@@ -781,7 +782,7 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) + wbits = wnd->bits_last; + + tail = wbits - wbit; +- op = tail < bits ? tail : bits; ++ op = min_t(u32, tail, bits); + + bh = wnd_map(wnd, iw); + if (IS_ERR(bh)) { +@@ -831,7 +832,7 @@ static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits) + wbits = wnd->bits_last; + + tail = wbits - wbit; +- op = tail < bits ? tail : bits; ++ op = min_t(u32, tail, bits); + + if (wbits != wnd->free_bits[iw]) { + bool ret; +@@ -923,7 +924,7 @@ bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits) + wbits = wnd->bits_last; + + tail = wbits - wbit; +- op = tail < bits ? tail : bits; ++ op = min_t(u32, tail, bits); + + if (wnd->free_bits[iw]) { + bool ret; +diff --git a/src/fsntfs.c b/src/fsntfs.c +index c964d3996aab1dff3923337acaa00e40869a70f5..77d2e56d5b4f34a693765d3230cf032122a04239 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -382,7 +382,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, + } + + lcn = wnd_zone_bit(wnd); +- alen = zlen > len ? len : zlen; ++ alen = min_t(CLST, len, zlen); + + wnd_zone_set(wnd, lcn + alen, zlen - alen); + +@@ -1096,7 +1096,7 @@ int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, + len = ((u64)clen << cluster_bits) - off; + + for (;;) { +- u32 op = len < bytes ? len : bytes; ++ u32 op = min_t(u64, len, bytes); + int err = ntfs_sb_write(sb, lbo, op, buf, 0); + + if (err) +@@ -1297,7 +1297,7 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, + nb->off = off = lbo & (blocksize - 1); + + for (;;) { +- u32 len32 = len < bytes ? len : bytes; ++ u32 len32 = min_t(u64, len, bytes); + sector_t block = lbo >> sb->s_blocksize_bits; + + do { +-- +2.31.1 + diff --git a/SOURCES/0063-fs-ntfs3-Fix-wrong-error-message-Logfile-UpCase.patch b/SOURCES/0063-fs-ntfs3-Fix-wrong-error-message-Logfile-UpCase.patch new file mode 100644 index 0000000..fea2612 --- /dev/null +++ b/SOURCES/0063-fs-ntfs3-Fix-wrong-error-message-Logfile-UpCase.patch @@ -0,0 +1,31 @@ +From 0412016e48076f5f9dbdcc6613436e3c6db89523 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:32 +0300 +Subject: [Backport 0412016e4807] src: Fix wrong error message $Logfile -> + $UpCase + +Fix wrong error message $Logfile -> $UpCase. Probably copy paste. + +Fixes: 203c2b3a406a ("src: Add initialization of super block") +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/super.c b/src/super.c +index acfa00acf4dd1c94ebfc63a47270a86570e0073e..1e01184f4ee251c05f6df6a54ea29e289dc11a52 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1201,7 +1201,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_UPCASE); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); +- ntfs_err(sb, "Failed to load \x24LogFile."); ++ ntfs_err(sb, "Failed to load $UpCase."); + inode = NULL; + goto out; + } +-- +2.31.1 + diff --git a/SOURCES/0064-fs-ntfs3-Change-EINVAL-to-ENOMEM-when-d_make_root-fa.patch b/SOURCES/0064-fs-ntfs3-Change-EINVAL-to-ENOMEM-when-d_make_root-fa.patch new file mode 100644 index 0000000..790e5d4 --- /dev/null +++ b/SOURCES/0064-fs-ntfs3-Change-EINVAL-to-ENOMEM-when-d_make_root-fa.patch @@ -0,0 +1,31 @@ +From 7ea04817866a5ac369f1a33d1b86d06a695aaedb Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:33 +0300 +Subject: [Backport 7ea04817866a] src: Change EINVAL to ENOMEM when + d_make_root fails + +Change EINVAL to ENOMEM when d_make_root fails because that is right +errno. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/super.c b/src/super.c +index 1e01184f4ee251c05f6df6a54ea29e289dc11a52..642bd63932d09edef15321dc3878ba3e3eb6d4b1 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1284,7 +1284,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + sb->s_root = d_make_root(inode); + + if (!sb->s_root) { +- err = -EINVAL; ++ err = -ENOMEM; + goto out; + } + +-- +2.31.1 + diff --git a/SOURCES/0065-fs-ntfs3-Remove-impossible-fault-condition-in-fill_s.patch b/SOURCES/0065-fs-ntfs3-Remove-impossible-fault-condition-in-fill_s.patch new file mode 100644 index 0000000..70ce1a0 --- /dev/null +++ b/SOURCES/0065-fs-ntfs3-Remove-impossible-fault-condition-in-fill_s.patch @@ -0,0 +1,35 @@ +From 5d7d6b16bc1dbe0f84997e639c49b5ed98a562f7 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:34 +0300 +Subject: [Backport 5d7d6b16bc1d] src: Remove impossible fault condition + in fill_super + +Remove root drop when we fault out. This can never happened because +when we allocate root we eather fault when no root or success. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 642bd63932d09edef15321dc3878ba3e3eb6d4b1..ad185c723c0ee44506f88c96ae4b3497f623fe79 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1295,12 +1295,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + out: + iput(inode); +- +- if (sb->s_root) { +- d_drop(sb->s_root); +- sb->s_root = NULL; +- } +- + return err; + } + +-- +2.31.1 + diff --git a/SOURCES/0066-fs-ntfs3-Return-straight-without-goto-in-fill_super.patch b/SOURCES/0066-fs-ntfs3-Return-straight-without-goto-in-fill_super.patch new file mode 100644 index 0000000..cdb2035 --- /dev/null +++ b/SOURCES/0066-fs-ntfs3-Return-straight-without-goto-in-fill_super.patch @@ -0,0 +1,194 @@ +From bce1828f6d82ad0ffa3b7259d6f1769ffbcec30c Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:35 +0300 +Subject: [Backport bce1828f6d82] src: Return straight without goto in + fill_super + +In many places it is not needed to use goto out. We can just return +right away. This will make code little bit more cleaner as we won't +need to check error path. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 56 ++++++++++++++---------------------------------- + 1 file changed, 16 insertions(+), 40 deletions(-) + +diff --git a/src/super.c b/src/super.c +index ad185c723c0ee44506f88c96ae4b3497f623fe79..31a9a0d16261d77ef50a1291fc5e8710c80b7864 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -921,7 +921,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512, + bd_inode->i_size); + if (err) +- goto out; ++ return err; + + #ifdef CONFIG_NTFS3_64BIT_CLUSTER + sb->s_maxbytes = MAX_LFS_FILESIZE; +@@ -937,10 +937,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_VOL); + inode = ntfs_iget5(sb, &ref, &NAME_VOLUME); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $Volume."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); +@@ -988,10 +986,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_MIRR); + inode = ntfs_iget5(sb, &ref, &NAME_MIRROR); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $MFTMirr."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + sbi->mft.recs_mirr = +@@ -1004,10 +1000,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_LOG); + inode = ntfs_iget5(sb, &ref, &NAME_LOGFILE); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load \x24LogFile."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); +@@ -1025,16 +1019,14 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + if (!is_ro) { + ntfs_warn(sb, + "failed to replay log file. Can't mount rw!"); +- err = -EINVAL; +- goto out; ++ return -EINVAL; + } + } else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) { + if (!is_ro && !sbi->options->force) { + ntfs_warn( + sb, + "volume is dirty and \"force\" flag is not set!"); +- err = -EINVAL; +- goto out; ++ return -EINVAL; + } + } + +@@ -1044,10 +1036,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + inode = ntfs_iget5(sb, &ref, &NAME_MFT); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $MFT."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); +@@ -1071,10 +1061,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_BADCLUST); + inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $BadClus."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); +@@ -1096,10 +1084,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_BITMAP); + inode = ntfs_iget5(sb, &ref, &NAME_BITMAP); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $Bitmap."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); +@@ -1129,17 +1115,15 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + /* Compute the MFT zone. */ + err = ntfs_refresh_zone(sbi); + if (err) +- goto out; ++ return err; + + /* Load $AttrDef. */ + ref.low = cpu_to_le32(MFT_REC_ATTR); + ref.seq = cpu_to_le16(MFT_REC_ATTR); + inode = ntfs_iget5(sbi->sb, &ref, &NAME_ATTRDEF); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $AttrDef -> %d", err); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + if (inode->i_size < sizeof(struct ATTR_DEF_ENTRY)) { +@@ -1200,10 +1184,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_UPCASE); + inode = ntfs_iget5(sb, &ref, &NAME_UPCASE); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load $UpCase."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); +@@ -1249,7 +1231,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + /* Load $Secure. */ + err = ntfs_security_init(sbi); + if (err) +- goto out; ++ return err; + + /* Load $Extend. */ + err = ntfs_extend_init(sbi); +@@ -1273,26 +1255,20 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ref.seq = cpu_to_le16(MFT_REC_ROOT); + inode = ntfs_iget5(sb, &ref, &NAME_ROOT); + if (IS_ERR(inode)) { +- err = PTR_ERR(inode); + ntfs_err(sb, "Failed to load root."); +- inode = NULL; +- goto out; ++ return PTR_ERR(inode); + } + + ni = ntfs_i(inode); + + sb->s_root = d_make_root(inode); +- +- if (!sb->s_root) { +- err = -ENOMEM; +- goto out; +- } ++ if (!sb->s_root) ++ return -ENOMEM; + + fc->fs_private = NULL; + fc->s_fs_info = NULL; + + return 0; +- + out: + iput(inode); + return err; +-- +2.31.1 + diff --git a/SOURCES/0067-fs-ntfs3-Remove-unnecessary-variable-loading-in-fill.patch b/SOURCES/0067-fs-ntfs3-Remove-unnecessary-variable-loading-in-fill.patch new file mode 100644 index 0000000..7b1ee92 --- /dev/null +++ b/SOURCES/0067-fs-ntfs3-Remove-unnecessary-variable-loading-in-fill.patch @@ -0,0 +1,84 @@ +From 10b4f12c7028399007aaebc25810b18d8f7731a7 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:36 +0300 +Subject: [Backport 10b4f12c7028] src: Remove unnecessary variable loading + in fill_super + +Remove some unnecessary variable loading. These look like copy paste +work and they are not used to anything. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 31a9a0d16261d77ef50a1291fc5e8710c80b7864..4ff61534682cd903bcea5f8c73567f10006e3a4d 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -877,7 +877,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + struct block_device *bdev = sb->s_bdev; + struct inode *bd_inode = bdev->bd_inode; + struct request_queue *rq = bdev_get_queue(bdev); +- struct inode *inode = NULL; ++ struct inode *inode; + struct ntfs_inode *ni; + size_t i, tt; + CLST vcn, lcn, len; +@@ -977,9 +977,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + sbi->volume.major_ver = info->major_ver; + sbi->volume.minor_ver = info->minor_ver; + sbi->volume.flags = info->flags; +- + sbi->volume.ni = ni; +- inode = NULL; + + /* Load $MFTMirr to estimate recs_mirr. */ + ref.low = cpu_to_le32(MFT_REC_MIRR); +@@ -1011,7 +1009,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + goto out; + + iput(inode); +- inode = NULL; + + is_ro = sb_rdonly(sbi->sb); + +@@ -1088,8 +1085,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + return PTR_ERR(inode); + } + +- ni = ntfs_i(inode); +- + #ifndef CONFIG_NTFS3_64BIT_CLUSTER + if (inode->i_size >> 32) { + err = -EINVAL; +@@ -1188,8 +1183,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + return PTR_ERR(inode); + } + +- ni = ntfs_i(inode); +- + if (inode->i_size != 0x10000 * sizeof(short)) { + err = -EINVAL; + goto out; +@@ -1225,7 +1218,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + } + + iput(inode); +- inode = NULL; + + if (is_ntfs3(sbi)) { + /* Load $Secure. */ +@@ -1259,8 +1251,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + return PTR_ERR(inode); + } + +- ni = ntfs_i(inode); +- + sb->s_root = d_make_root(inode); + if (!sb->s_root) + return -ENOMEM; +-- +2.31.1 + diff --git a/SOURCES/0068-fs-ntfs3-Use-sb-instead-of-sbi-sb-in-fill_super.patch b/SOURCES/0068-fs-ntfs3-Use-sb-instead-of-sbi-sb-in-fill_super.patch new file mode 100644 index 0000000..5f63bb1 --- /dev/null +++ b/SOURCES/0068-fs-ntfs3-Use-sb-instead-of-sbi-sb-in-fill_super.patch @@ -0,0 +1,49 @@ +From b4f110d65e21bf937fc5d3d894ec1cd540c85a71 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:37 +0300 +Subject: [Backport b4f110d65e21] src: Use sb instead of sbi->sb in + fill_super + +Use sb instead of sbi->sb in fill_super. We have sb so why not use +it. Also makes code more readable. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 4ff61534682cd903bcea5f8c73567f10006e3a4d..c18705bf857f6f58c56c619a9702fa02dc7a30b6 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1010,7 +1010,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + iput(inode); + +- is_ro = sb_rdonly(sbi->sb); ++ is_ro = sb_rdonly(sb); + + if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) { + if (!is_ro) { +@@ -1101,7 +1101,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + /* Not necessary. */ + sbi->used.bitmap.set_tail = true; +- err = wnd_init(&sbi->used.bitmap, sbi->sb, tt); ++ err = wnd_init(&sbi->used.bitmap, sb, tt); + if (err) + goto out; + +@@ -1115,7 +1115,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + /* Load $AttrDef. */ + ref.low = cpu_to_le32(MFT_REC_ATTR); + ref.seq = cpu_to_le16(MFT_REC_ATTR); +- inode = ntfs_iget5(sbi->sb, &ref, &NAME_ATTRDEF); ++ inode = ntfs_iget5(sb, &ref, &NAME_ATTRDEF); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $AttrDef -> %d", err); + return PTR_ERR(inode); +-- +2.31.1 + diff --git a/SOURCES/0069-fs-ntfs3-Remove-tmp-var-is_ro-in-ntfs_fill_super.patch b/SOURCES/0069-fs-ntfs3-Remove-tmp-var-is_ro-in-ntfs_fill_super.patch new file mode 100644 index 0000000..5cfe4ab --- /dev/null +++ b/SOURCES/0069-fs-ntfs3-Remove-tmp-var-is_ro-in-ntfs_fill_super.patch @@ -0,0 +1,51 @@ +From 0cde7e81cd448a5df01a3960f6608b15dc3f12a3 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:38 +0300 +Subject: [Backport 0cde7e81cd44] src: Remove tmp var is_ro in + ntfs_fill_super + +We only use this in two places so we do not really need it. Also +wrapper sb_rdonly() is pretty self explanatory. This will make little +bit easier to read this super long variable list in the beginning of +ntfs_fill_super(). + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/src/super.c b/src/super.c +index c18705bf857f6f58c56c619a9702fa02dc7a30b6..93b8a1f4d7483210eeac016f2fb8abaa7e5d8b2c 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -887,7 +887,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + struct ATTR_DEF_ENTRY *t; + u16 *upcase; + u16 *shared; +- bool is_ro; + struct MFT_REF ref; + + ref.high = 0; +@@ -1010,16 +1009,14 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + iput(inode); + +- is_ro = sb_rdonly(sb); +- + if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) { +- if (!is_ro) { ++ if (!sb_rdonly(sb)) { + ntfs_warn(sb, + "failed to replay log file. Can't mount rw!"); + return -EINVAL; + } + } else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) { +- if (!is_ro && !sbi->options->force) { ++ if (!sb_rdonly(sb) && !sbi->options->force) { + ntfs_warn( + sb, + "volume is dirty and \"force\" flag is not set!"); +-- +2.31.1 + diff --git a/SOURCES/0070-fs-ntfs3-Remove-tmp-pointer-bd_inode-in-fill_super.patch b/SOURCES/0070-fs-ntfs3-Remove-tmp-pointer-bd_inode-in-fill_super.patch new file mode 100644 index 0000000..fb50c49 --- /dev/null +++ b/SOURCES/0070-fs-ntfs3-Remove-tmp-pointer-bd_inode-in-fill_super.patch @@ -0,0 +1,40 @@ +From 4ea41b3eb5fd51b47742c6fa2ac1851a51ab0c69 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:39 +0300 +Subject: [Backport 4ea41b3eb5fd] src: Remove tmp pointer bd_inode in + fill_super + +Drop tmp pointer bd_inode because this is only used ones in fill_super. +Also we have so many initializing happening at the beginning that it is +already way too much to follow. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 93b8a1f4d7483210eeac016f2fb8abaa7e5d8b2c..e5ec808bd0522cb986ef11cbcc3ece57f6988916 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -875,7 +875,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + int err; + struct ntfs_sb_info *sbi = sb->s_fs_info; + struct block_device *bdev = sb->s_bdev; +- struct inode *bd_inode = bdev->bd_inode; + struct request_queue *rq = bdev_get_queue(bdev); + struct inode *inode; + struct ntfs_inode *ni; +@@ -918,7 +917,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + /* Parse boot. */ + err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512, +- bd_inode->i_size); ++ bdev->bd_inode->i_size); + if (err) + return err; + +-- +2.31.1 + diff --git a/SOURCES/0071-fs-ntfs3-Remove-tmp-pointer-upcase-in-fill_super.patch b/SOURCES/0071-fs-ntfs3-Remove-tmp-pointer-upcase-in-fill_super.patch new file mode 100644 index 0000000..fbc5ea5 --- /dev/null +++ b/SOURCES/0071-fs-ntfs3-Remove-tmp-pointer-upcase-in-fill_super.patch @@ -0,0 +1,57 @@ +From 0056b273757b3057a5aff73f96a7fa134641caf4 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:40 +0300 +Subject: [Backport 0056b273757b] src: Remove tmp pointer upcase in + fill_super + +We can survive without this tmp point upcase. So remove it we don't have +so many tmp pointer in this function. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/src/super.c b/src/super.c +index e5ec808bd0522cb986ef11cbcc3ece57f6988916..8ce0d4f439a1e26b9f12dbdab0fbb946da859adb 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -884,7 +884,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + const struct VOLUME_INFO *info; + u32 idx, done, bytes; + struct ATTR_DEF_ENTRY *t; +- u16 *upcase; + u16 *shared; + struct MFT_REF ref; + +@@ -1184,11 +1183,9 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + goto out; + } + +- upcase = sbi->upcase; +- + for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) { + const __le16 *src; +- u16 *dst = Add2Ptr(upcase, idx << PAGE_SHIFT); ++ u16 *dst = Add2Ptr(sbi->upcase, idx << PAGE_SHIFT); + struct page *page = ntfs_map_page(inode->i_mapping, idx); + + if (IS_ERR(page)) { +@@ -1207,10 +1204,10 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ntfs_unmap_page(page); + } + +- shared = ntfs_set_shared(upcase, 0x10000 * sizeof(short)); +- if (shared && upcase != shared) { ++ shared = ntfs_set_shared(sbi->upcase, 0x10000 * sizeof(short)); ++ if (shared && sbi->upcase != shared) { ++ kvfree(sbi->upcase); + sbi->upcase = shared; +- kvfree(upcase); + } + + iput(inode); +-- +2.31.1 + diff --git a/SOURCES/0072-fs-ntfs3-Initialize-pointer-before-use-place-in-fill.patch b/SOURCES/0072-fs-ntfs3-Initialize-pointer-before-use-place-in-fill.patch new file mode 100644 index 0000000..69aea39 --- /dev/null +++ b/SOURCES/0072-fs-ntfs3-Initialize-pointer-before-use-place-in-fill.patch @@ -0,0 +1,46 @@ +From 0e59a87ee619915c2eb189eb232a972c276681fb Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:41 +0300 +Subject: [Backport 0e59a87ee619] src: Initialize pointer before use place + in fill_super + +Initializing should be as close as possible when we use it so that +we do not need to scroll up to see what is happening. + +Also bdev_get_queue() can never return NULL so we do not need to check +for !rq. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 8ce0d4f439a1e26b9f12dbdab0fbb946da859adb..3d034eac2bd197408e7e64a279799d68ad1a8129 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -875,7 +875,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + int err; + struct ntfs_sb_info *sbi = sb->s_fs_info; + struct block_device *bdev = sb->s_bdev; +- struct request_queue *rq = bdev_get_queue(bdev); ++ struct request_queue *rq; + struct inode *inode; + struct ntfs_inode *ni; + size_t i, tt; +@@ -904,9 +904,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + return -EINVAL; + } + +- if (!rq || !blk_queue_discard(rq) || !rq->limits.discard_granularity) { +- ; +- } else { ++ rq = bdev_get_queue(bdev); ++ if (blk_queue_discard(rq) && rq->limits.discard_granularity) { + sbi->discard_granularity = rq->limits.discard_granularity; + sbi->discard_granularity_mask_inv = + ~(u64)(sbi->discard_granularity - 1); +-- +2.31.1 + diff --git a/SOURCES/0073-fs-ntfs3-Initiliaze-sb-blocksize-only-in-one-place-r.patch b/SOURCES/0073-fs-ntfs3-Initiliaze-sb-blocksize-only-in-one-place-r.patch new file mode 100644 index 0000000..d9e6aae --- /dev/null +++ b/SOURCES/0073-fs-ntfs3-Initiliaze-sb-blocksize-only-in-one-place-r.patch @@ -0,0 +1,71 @@ +From 28861e3bbd9e7ac4cd9c811aad71b4d116e27930 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Thu, 9 Sep 2021 21:09:42 +0300 +Subject: [Backport 28861e3bbd9e] src: Initiliaze sb blocksize only in one + place + refactor + +Right now sb blocksize first get initiliazed in fill_super but in can be +changed in helper function. It makes more sense to that this happened +only in one place. + +Because we move this to helper function it makes more sense that +s_maxbytes will also be there. I rather have every sb releted thing in +fill_super, but because there is already sb releted stuff in this +helper. This will have to do for now. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 3d034eac2bd197408e7e64a279799d68ad1a8129..cefb9ddaf4db78890e9dc389416f47323bf33bcd 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -840,8 +840,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + rec->total = cpu_to_le32(sbi->record_size); + ((struct ATTRIB *)Add2Ptr(rec, ao))->type = ATTR_END; + +- if (sbi->cluster_size < PAGE_SIZE) +- sb_set_blocksize(sb, sbi->cluster_size); ++ sb_set_blocksize(sb, min_t(u32, sbi->cluster_size, PAGE_SIZE)); + + sbi->block_mask = sb->s_blocksize - 1; + sbi->blocks_per_cluster = sbi->cluster_size >> sb->s_blocksize_bits; +@@ -854,9 +853,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + if (clusters >= (1ull << (64 - sbi->cluster_bits))) + sbi->maxbytes = -1; + sbi->maxbytes_sparse = -1; ++ sb->s_maxbytes = MAX_LFS_FILESIZE; + #else + /* Maximum size for sparse file. */ + sbi->maxbytes_sparse = (1ull << (sbi->cluster_bits + 32)) - 1; ++ sb->s_maxbytes = 0xFFFFFFFFull << sbi->cluster_bits; + #endif + + err = 0; +@@ -911,20 +912,12 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + ~(u64)(sbi->discard_granularity - 1); + } + +- sb_set_blocksize(sb, PAGE_SIZE); +- + /* Parse boot. */ + err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512, + bdev->bd_inode->i_size); + if (err) + return err; + +-#ifdef CONFIG_NTFS3_64BIT_CLUSTER +- sb->s_maxbytes = MAX_LFS_FILESIZE; +-#else +- sb->s_maxbytes = 0xFFFFFFFFull << sbi->cluster_bits; +-#endif +- + /* + * Load $Volume. This should be done before $LogFile + * 'cause 'sbi->volume.ni' is used 'ntfs_set_state'. +-- +2.31.1 + diff --git a/SOURCES/0074-fs-ntfs3-Fix-a-memory-leak-on-object-opts.patch b/SOURCES/0074-fs-ntfs3-Fix-a-memory-leak-on-object-opts.patch new file mode 100644 index 0000000..6fc3cc4 --- /dev/null +++ b/SOURCES/0074-fs-ntfs3-Fix-a-memory-leak-on-object-opts.patch @@ -0,0 +1,39 @@ +From 880301bb313295a65523e79bc5666f5cf49eb3ed Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Fri, 10 Sep 2021 11:02:02 +0100 +Subject: [Backport 880301bb3132] src: Fix a memory leak on object opts + +Currently a failed allocation on sbi->upcase will cause an exit via +the label free_sbi causing a memory leak on object opts. Fix this by +re-ordering the exit paths free_opts and free_sbi so that kfree's occur +in the reverse allocation order. + +Addresses-Coverity: ("Resource leak") +Fixes: 27fac77707a1 ("src: Init spi more in init_fs_context than fill_super") +Signed-off-by: Colin Ian King +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/super.c b/src/super.c +index cefb9ddaf4db78890e9dc389416f47323bf33bcd..6a535b144ff961036704ba5a5ac5b1cab7ae9fb4 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -1393,10 +1393,10 @@ static int ntfs_init_fs_context(struct fs_context *fc) + fc->ops = &ntfs_context_ops; + + return 0; +-free_opts: +- kfree(opts); + free_sbi: + kfree(sbi); ++free_opts: ++ kfree(opts); + return -ENOMEM; + } + +-- +2.31.1 + diff --git a/SOURCES/0075-fs-ntfs3-Fix-insertion-of-attr-in-ni_ins_attr_ext.patch b/SOURCES/0075-fs-ntfs3-Fix-insertion-of-attr-in-ni_ins_attr_ext.patch new file mode 100644 index 0000000..f7aa1cd --- /dev/null +++ b/SOURCES/0075-fs-ntfs3-Fix-insertion-of-attr-in-ni_ins_attr_ext.patch @@ -0,0 +1,35 @@ +From ee9d4810aab95208541d2807f9d114f1d5edcee0 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Wed, 8 Sep 2021 18:39:53 +0300 +Subject: [Backport ee9d4810aab9] src: Fix insertion of attr in + ni_ins_attr_ext + +Do not try to insert attribute if there is no room in record. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/frecord.c b/src/frecord.c +index 1e1a09034f65b023872d86b866ee5b3907634506..0d00b2301a2f1fa380335ab91abd0afa53538053 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -953,6 +953,13 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le, + continue; + } + ++ /* ++ * Do not try to insert this attribute ++ * if there is no room in record. ++ */ ++ if (le32_to_cpu(mi->mrec->used) + asize > sbi->record_size) ++ continue; ++ + /* Try to insert attribute into this subrecord. */ + attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize, + name_off, svcn, ins_le); +-- +2.31.1 + diff --git a/SOURCES/0076-fs-ntfs3-Change-max-hardlinks-limit-to-4000.patch b/SOURCES/0076-fs-ntfs3-Change-max-hardlinks-limit-to-4000.patch new file mode 100644 index 0000000..dcec28c --- /dev/null +++ b/SOURCES/0076-fs-ntfs3-Change-max-hardlinks-limit-to-4000.patch @@ -0,0 +1,38 @@ +From 56eaeb10e2619081cc383febf6740a4c3e806777 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Thu, 9 Sep 2021 13:12:41 +0300 +Subject: [Backport 56eaeb10e261] src: Change max hardlinks limit to 4000 + +xfstest generic/041 works with 3003 hardlinks. +Because of this we raise hardlinks limit to 4000. +There are no drawbacks or regressions. +Theoretically we can raise all the way up to ffff, +but there is no practical use for this. + +Signed-off-by: Konstantin Komarov +--- + src/ntfs.h | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/src/ntfs.h b/src/ntfs.h +index 303a162c31587475adb8c3190b16fa451e9616a7..9cc396b117bfd9b02531ad6c6de89ded53bba7f3 100644 +--- a/src/ntfs.h ++++ b/src/ntfs.h +@@ -26,9 +26,11 @@ + + #define NTFS_NAME_LEN 255 + +-/* ntfs.sys used 500 maximum links on-disk struct allows up to 0xffff. */ +-#define NTFS_LINK_MAX 0x400 +-//#define NTFS_LINK_MAX 0xffff ++/* ++ * ntfs.sys used 500 maximum links on-disk struct allows up to 0xffff. ++ * xfstest generic/041 creates 3003 hardlinks. ++ */ ++#define NTFS_LINK_MAX 4000 + + /* + * Activate to use 64 bit clusters instead of 32 bits in ntfs.sys. +-- +2.31.1 + diff --git a/SOURCES/0077-fs-ntfs3-Add-sync-flag-to-ntfs_sb_write_run-and-al_u.patch b/SOURCES/0077-fs-ntfs3-Add-sync-flag-to-ntfs_sb_write_run-and-al_u.patch new file mode 100644 index 0000000..75868e9 --- /dev/null +++ b/SOURCES/0077-fs-ntfs3-Add-sync-flag-to-ntfs_sb_write_run-and-al_u.patch @@ -0,0 +1,204 @@ +From 6354467245ff8dd04b54e39790f2ee4d21d5419e Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Thu, 9 Sep 2021 13:15:20 +0300 +Subject: [Backport 6354467245ff] src: Add sync flag to ntfs_sb_write_run + and al_update + +This allows to wait only when it's requested. +It speeds up creation of hardlinks. + +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 2 +- + src/attrlist.c | 6 +++--- + src/frecord.c | 2 +- + src/fslog.c | 8 ++++---- + src/fsntfs.c | 8 ++++---- + src/inode.c | 2 +- + src/ntfs_fs.h | 4 ++-- + src/xattr.c | 2 +- + 8 files changed, 17 insertions(+), 17 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index c15cc17e3cd92864f30fd0d3fbb8f20b98437cde..8a00fa978f5f9c3d496bd333bf1ace72d9194f37 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -287,7 +287,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, + if (!rsize) { + /* Empty resident -> Non empty nonresident. */ + } else if (!is_data) { +- err = ntfs_sb_write_run(sbi, run, 0, data, rsize); ++ err = ntfs_sb_write_run(sbi, run, 0, data, rsize, 0); + if (err) + goto out2; + } else if (!page) { +diff --git a/src/attrlist.c b/src/attrlist.c +index b9da527b96aa2ab00bf43d439d8527b587946a9d..bad6d8a849a24b3a4a9fc2c4c372496d6ac1b54a 100644 +--- a/src/attrlist.c ++++ b/src/attrlist.c +@@ -333,7 +333,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, + + if (attr && attr->non_res) { + err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, +- al->size); ++ al->size, 0); + if (err) + return err; + al->dirty = false; +@@ -420,7 +420,7 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, + return true; + } + +-int al_update(struct ntfs_inode *ni) ++int al_update(struct ntfs_inode *ni, int sync) + { + int err; + struct ATTRIB *attr; +@@ -442,7 +442,7 @@ int al_update(struct ntfs_inode *ni) + memcpy(resident_data(attr), al->le, al->size); + } else { + err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, +- al->size); ++ al->size, sync); + if (err) + goto out; + +diff --git a/src/frecord.c b/src/frecord.c +index 0d00b2301a2f1fa380335ab91abd0afa53538053..9a53f809576d0b795ebb6ed2834792a60be463b0 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -3208,7 +3208,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint) + goto out; + } + +- err = al_update(ni); ++ err = al_update(ni, sync); + if (err) + goto out; + } +diff --git a/src/fslog.c b/src/fslog.c +index 6e7f9b72792b354a6e588b506c7352b286ed58bd..06492f088d6020592bd4fdb8b1a6ecd22b823324 100644 +--- a/src/fslog.c ++++ b/src/fslog.c +@@ -2215,7 +2215,7 @@ static int last_log_lsn(struct ntfs_log *log) + + err = ntfs_sb_write_run(log->ni->mi.sbi, + &log->ni->file.run, off, page, +- log->page_size); ++ log->page_size, 0); + + if (err) + goto out; +@@ -3706,7 +3706,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, + + if (a_dirty) { + attr = oa->attr; +- err = ntfs_sb_write_run(sbi, oa->run1, vbo, buffer_le, bytes); ++ err = ntfs_sb_write_run(sbi, oa->run1, vbo, buffer_le, bytes, 0); + if (err) + goto out; + } +@@ -5148,10 +5148,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) + + ntfs_fix_pre_write(&rh->rhdr, log->page_size); + +- err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rh, log->page_size); ++ err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rh, log->page_size, 0); + if (!err) + err = ntfs_sb_write_run(sbi, &log->ni->file.run, log->page_size, +- rh, log->page_size); ++ rh, log->page_size, 0); + + kfree(rh); + if (err) +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 77d2e56d5b4f34a693765d3230cf032122a04239..4de9acb1696898057fe3a096984ab3c6c1119810 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -1077,7 +1077,7 @@ int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes, + } + + int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, +- u64 vbo, const void *buf, size_t bytes) ++ u64 vbo, const void *buf, size_t bytes, int sync) + { + struct super_block *sb = sbi->sb; + u8 cluster_bits = sbi->cluster_bits; +@@ -1097,7 +1097,7 @@ int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, + + for (;;) { + u32 op = min_t(u64, len, bytes); +- int err = ntfs_sb_write(sb, lbo, op, buf, 0); ++ int err = ntfs_sb_write(sb, lbo, op, buf, sync); + + if (err) + return err; +@@ -2172,7 +2172,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + + /* Write main SDS bucket. */ + err = ntfs_sb_write_run(sbi, &ni->file.run, sbi->security.next_off, +- d_security, aligned_sec_size); ++ d_security, aligned_sec_size, 0); + + if (err) + goto out; +@@ -2190,7 +2190,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi, + + /* Write copy SDS bucket. */ + err = ntfs_sb_write_run(sbi, &ni->file.run, mirr_off, d_security, +- aligned_sec_size); ++ aligned_sec_size, 0); + if (err) + goto out; + +diff --git a/src/inode.c b/src/inode.c +index 33f278a0947ca23e103683b9daece8f539da355b..d583b71bec502f2042764e27f565e20be1858394 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1584,7 +1584,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + /* Write non resident data. */ + if (nsize) { +- err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rp, nsize); ++ err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rp, nsize, 0); + if (err) + goto out7; + } +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index dae6dd4ac6198fe735749b8e1405e110da4ddd62..6731b5d9e2d8efc742ce2200528e81eb22cbfa6e 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -465,7 +465,7 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le); + bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, + const __le16 *name, size_t name_len, + const struct MFT_REF *ref); +-int al_update(struct ntfs_inode *ni); ++int al_update(struct ntfs_inode *ni, int sync); + static inline size_t al_aligned(size_t size) + { + return (size + 1023) & ~(size_t)1023; +@@ -606,7 +606,7 @@ int ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer); + int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes, + const void *buffer, int wait); + int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, +- u64 vbo, const void *buf, size_t bytes); ++ u64 vbo, const void *buf, size_t bytes, int sync); + struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi, + const struct runs_tree *run, u64 vbo); + int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run, +diff --git a/src/xattr.c b/src/xattr.c +index a8c5a899f0dd308fe5014bf3b384bc903ca0bf68..5c7c5c7a5ec1607cbe37c5cc6a847fd04a0d0464 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -441,7 +441,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + /* Delete xattr, ATTR_EA */ + ni_remove_attr_le(ni, attr, mi, le); + } else if (attr->non_res) { +- err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size); ++ err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size, 0); + if (err) + goto out; + } else { +-- +2.31.1 + diff --git a/SOURCES/0078-fs-ntfs3-Remove-a-useless-test-in-indx_find.patch b/SOURCES/0078-fs-ntfs3-Remove-a-useless-test-in-indx_find.patch new file mode 100644 index 0000000..b935710 --- /dev/null +++ b/SOURCES/0078-fs-ntfs3-Remove-a-useless-test-in-indx_find.patch @@ -0,0 +1,38 @@ +From d2846bf33c1423ff872c7a7c2afde292ad502c04 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sat, 18 Sep 2021 21:56:19 +0200 +Subject: [Backport d2846bf33c14] src: Remove a useless test in + 'indx_find()' + +'fnd' has been dereferenced several time before, so testing it here is +pointless. +Moreover, all callers of 'indx_find()' already have some error handling +code that makes sure that no NULL 'fnd' is passed. + +So, remove the useless test. + +Signed-off-by: Christophe JAILLET +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/index.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/index.c b/src/index.c +index 4f71a91f07d9fc56efe6a80ebf52995e2e9a65de..6f81e3a49abfb30f7aa570e26cf595b3e8599032 100644 +--- a/src/index.c ++++ b/src/index.c +@@ -1072,9 +1072,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni, + if (!e) + return -EINVAL; + +- if (fnd) +- fnd->root_de = e; +- ++ fnd->root_de = e; + err = 0; + + for (;;) { +-- +2.31.1 + diff --git a/SOURCES/0079-fs-ntfs3-Remove-a-useless-shadowing-variable.patch b/SOURCES/0079-fs-ntfs3-Remove-a-useless-shadowing-variable.patch new file mode 100644 index 0000000..7f48b4a --- /dev/null +++ b/SOURCES/0079-fs-ntfs3-Remove-a-useless-shadowing-variable.patch @@ -0,0 +1,34 @@ +From 808bc0a82bcd2cbe32a139613325b1a3e03f35f1 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sat, 18 Sep 2021 21:56:28 +0200 +Subject: [Backport 808bc0a82bcd] src: Remove a useless shadowing variable + +There is already a 'u8 mask' defined at the top of the function. +There is no need to define a new one here. + +Remove the useless and shadowing new 'mask' variable. + +Signed-off-by: Christophe JAILLET +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/bitfunc.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/bitfunc.c b/src/bitfunc.c +index bf10e2da5c6e3d29d3788055b2b664927cc9f29b..50d838093790a1f07474ea201510dde42d2431f3 100644 +--- a/src/bitfunc.c ++++ b/src/bitfunc.c +@@ -119,8 +119,7 @@ bool are_bits_set(const ulong *lmap, size_t bit, size_t nbits) + + pos = nbits & 7; + if (pos) { +- u8 mask = fill_mask[pos]; +- ++ mask = fill_mask[pos]; + if ((*map & mask) != mask) + return false; + } +-- +2.31.1 + diff --git a/SOURCES/0080-fs-ntfs3-Remove-deprecated-mount-options-nls.patch b/SOURCES/0080-fs-ntfs3-Remove-deprecated-mount-options-nls.patch new file mode 100644 index 0000000..5141f9d --- /dev/null +++ b/SOURCES/0080-fs-ntfs3-Remove-deprecated-mount-options-nls.patch @@ -0,0 +1,37 @@ +From 82cb875313188ccbd3ac174b471c86dcb97b8626 Mon Sep 17 00:00:00 2001 +From: Kari Argillander +Date: Tue, 21 Sep 2021 20:19:01 +0300 +Subject: [Backport 82cb87531318] src: Remove deprecated mount options nls + +Some discussion has been spoken that this deprecated mount options +should be removed before 5.15 lands. This driver is not never seen day +light so it was decided that nls mount option has to be removed. We have +always possibility to add this if needed. + +One possible need is example if current ntfs driver will be taken out of +kernel and ntfs3 needs to support mount options what it has. + +Signed-off-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 6a535b144ff961036704ba5a5ac5b1cab7ae9fb4..800897777eb03855f702e8acf15b39d02c1c2fbf 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -246,10 +246,6 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = { + fsparam_flag_no("prealloc", Opt_prealloc), + fsparam_flag_no("acsrules", Opt_noacsrules), + fsparam_string("iocharset", Opt_iocharset), +- +- __fsparam(fs_param_is_string, +- "nls", Opt_iocharset, +- fs_param_deprecated, NULL), + {} + }; + +-- +2.31.1 + diff --git a/SOURCES/0081-fs-ntfs3-Fix-logical-error-in-ntfs_create_inode.patch b/SOURCES/0081-fs-ntfs3-Fix-logical-error-in-ntfs_create_inode.patch new file mode 100644 index 0000000..b419d6b --- /dev/null +++ b/SOURCES/0081-fs-ntfs3-Fix-logical-error-in-ntfs_create_inode.patch @@ -0,0 +1,31 @@ +From 6c1ee4d304983d2f68bf7639d5912f781398d5ac Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Thu, 23 Sep 2021 18:05:36 +0300 +Subject: [Backport 6c1ee4d30498] src: Fix logical error in + ntfs_create_inode + +We need to always call indx_delete_entry after indx_insert_entry +if error occurred. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/inode.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/inode.c b/src/inode.c +index d583b71bec502f2042764e27f565e20be1858394..d51bf40188357e8c292b7ddbbee100d3dfbaeff5 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1575,7 +1575,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) { + err = ntfs_init_acl(mnt_userns, inode, dir); + if (err) +- goto out6; ++ goto out7; + } else + #endif + { +-- +2.31.1 + diff --git a/SOURCES/0082-fs-ntfs3-Move-ni_lock_dir-and-ni_unlock-into-ntfs_cr.patch b/SOURCES/0082-fs-ntfs3-Move-ni_lock_dir-and-ni_unlock-into-ntfs_cr.patch new file mode 100644 index 0000000..ee6d011 --- /dev/null +++ b/SOURCES/0082-fs-ntfs3-Move-ni_lock_dir-and-ni_unlock-into-ntfs_cr.patch @@ -0,0 +1,144 @@ +From d562e901f25d275b55b0496fe01935beeff0ed37 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Thu, 23 Sep 2021 18:04:26 +0300 +Subject: [Backport d562e901f25d] src: Move ni_lock_dir and ni_unlock into + ntfs_create_inode + +Now ntfs3 locks mutex for smaller time. +Theoretically in successful cases those locks aren't needed at all. +But proving the same for error cases is difficult. +So instead of removing them we just move them. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/inode.c | 17 ++++++++++++++--- + src/namei.c | 20 -------------------- + 2 files changed, 14 insertions(+), 23 deletions(-) + +diff --git a/src/inode.c b/src/inode.c +index d51bf40188357e8c292b7ddbbee100d3dfbaeff5..7dd162f6a7e26435b16c08b5efed6e283b975f54 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1198,9 +1198,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + struct REPARSE_DATA_BUFFER *rp = NULL; + bool rp_inserted = false; + ++ ni_lock_dir(dir_ni); ++ + dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL); +- if (!dir_root) +- return ERR_PTR(-EINVAL); ++ if (!dir_root) { ++ err = -EINVAL; ++ goto out1; ++ } + + if (S_ISDIR(mode)) { + /* Use parent's directory attributes. */ +@@ -1549,6 +1553,9 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + if (err) + goto out6; + ++ /* Unlock parent directory before ntfs_init_acl. */ ++ ni_unlock(dir_ni); ++ + inode->i_generation = le16_to_cpu(rec->seq); + + dir->i_mtime = dir->i_ctime = inode->i_atime; +@@ -1605,8 +1612,10 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + out7: + + /* Undo 'indx_insert_entry'. */ ++ ni_lock_dir(dir_ni); + indx_delete_entry(&dir_ni->dir, dir_ni, new_de + 1, + le16_to_cpu(new_de->key_size), sbi); ++ /* ni_unlock(dir_ni); will be called later. */ + out6: + if (rp_inserted) + ntfs_remove_reparse(sbi, IO_REPARSE_TAG_SYMLINK, &new_de->ref); +@@ -1630,8 +1639,10 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + kfree(rp); + + out1: +- if (err) ++ if (err) { ++ ni_unlock(dir_ni); + return ERR_PTR(err); ++ } + + unlock_new_inode(inode); + +diff --git a/src/namei.c b/src/namei.c +index 1c475da4e19d1158e3c11fcb749fb0825d2d7ae6..bc741213ad84833b7fd434c32d4b784c6e7cb2f7 100644 +--- a/src/namei.c ++++ b/src/namei.c +@@ -95,16 +95,11 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, + static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) + { +- struct ntfs_inode *ni = ntfs_i(dir); + struct inode *inode; + +- ni_lock_dir(ni); +- + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFREG | mode, + 0, NULL, 0, NULL); + +- ni_unlock(ni); +- + return IS_ERR(inode) ? PTR_ERR(inode) : 0; + } + +@@ -116,16 +111,11 @@ static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, + static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t rdev) + { +- struct ntfs_inode *ni = ntfs_i(dir); + struct inode *inode; + +- ni_lock_dir(ni); +- + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, mode, rdev, + NULL, 0, NULL); + +- ni_unlock(ni); +- + return IS_ERR(inode) ? PTR_ERR(inode) : 0; + } + +@@ -196,15 +186,10 @@ static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, + { + u32 size = strlen(symname); + struct inode *inode; +- struct ntfs_inode *ni = ntfs_i(dir); +- +- ni_lock_dir(ni); + + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFLNK | 0777, + 0, symname, size, NULL); + +- ni_unlock(ni); +- + return IS_ERR(inode) ? PTR_ERR(inode) : 0; + } + +@@ -215,15 +200,10 @@ static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, + struct dentry *dentry, umode_t mode) + { + struct inode *inode; +- struct ntfs_inode *ni = ntfs_i(dir); +- +- ni_lock_dir(ni); + + inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFDIR | mode, + 0, NULL, 0, NULL); + +- ni_unlock(ni); +- + return IS_ERR(inode) ? PTR_ERR(inode) : 0; + } + +-- +2.31.1 + diff --git a/SOURCES/0083-fs-ntfs3-Refactor-ntfs_get_acl_ex-for-better-readabi.patch b/SOURCES/0083-fs-ntfs3-Refactor-ntfs_get_acl_ex-for-better-readabi.patch new file mode 100644 index 0000000..c405165 --- /dev/null +++ b/SOURCES/0083-fs-ntfs3-Refactor-ntfs_get_acl_ex-for-better-readabi.patch @@ -0,0 +1,40 @@ +From 0bd5fdb811b0449fcef948a100099cc3197f1b73 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Wed, 22 Sep 2021 18:28:36 +0300 +Subject: [Backport 0bd5fdb811b0] src: Refactor ntfs_get_acl_ex for better + readability + +We can safely move set_cached_acl because it works with NULL acl too. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 5c7c5c7a5ec1607cbe37c5cc6a847fd04a0d0464..3795943efc8e36d64710d8467ce48ef72c2121cd 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -518,12 +518,15 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + /* Translate extended attribute to acl. */ + if (err >= 0) { + acl = posix_acl_from_xattr(mnt_userns, buf, err); +- if (!IS_ERR(acl)) +- set_cached_acl(inode, type, acl); ++ } else if (err == -ENODATA) { ++ acl = NULL; + } else { +- acl = err == -ENODATA ? NULL : ERR_PTR(err); ++ acl = ERR_PTR(err); + } + ++ if (!IS_ERR(acl)) ++ set_cached_acl(inode, type, acl); ++ + __putname(buf); + + return acl; +-- +2.31.1 + diff --git a/SOURCES/0084-fs-ntfs3-Pass-flags-to-ntfs_set_ea-in-ntfs_set_acl_e.patch b/SOURCES/0084-fs-ntfs3-Pass-flags-to-ntfs_set_ea-in-ntfs_set_acl_e.patch new file mode 100644 index 0000000..1eaa008 --- /dev/null +++ b/SOURCES/0084-fs-ntfs3-Pass-flags-to-ntfs_set_ea-in-ntfs_set_acl_e.patch @@ -0,0 +1,58 @@ +From 398c35f4d78410725e154c2fc0a51ecac05a3f1f Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Wed, 22 Sep 2021 18:33:38 +0300 +Subject: [Backport 398c35f4d784] src: Pass flags to ntfs_set_ea in + ntfs_set_acl_ex + +In case of removing of xattr there must be XATTR_REPLACE flag and +zero length. We already check XATTR_REPLACE in ntfs_set_ea, so +now we pass XATTR_REPLACE to ntfs_set_ea. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 3795943efc8e36d64710d8467ce48ef72c2121cd..70f2f9eb6b1eb3e7bebc3703762abfb7e42d7be1 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -549,6 +549,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + size_t size, name_len; + void *value = NULL; + int err = 0; ++ int flags; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; +@@ -591,20 +592,24 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + } + + if (!acl) { ++ /* Remove xattr if it can be presented via mode. */ + size = 0; + value = NULL; ++ flags = XATTR_REPLACE; + } else { + size = posix_acl_xattr_size(acl->a_count); + value = kmalloc(size, GFP_NOFS); + if (!value) + return -ENOMEM; +- + err = posix_acl_to_xattr(mnt_userns, acl, value, size); + if (err < 0) + goto out; ++ flags = 0; + } + +- err = ntfs_set_ea(inode, name, name_len, value, size, 0, locked); ++ err = ntfs_set_ea(inode, name, name_len, value, size, flags, locked); ++ if (err == -ENODATA && !size) ++ err = 0; /* Removing non existed xattr. */ + if (!err) + set_cached_acl(inode, type, acl); + +-- +2.31.1 + diff --git a/SOURCES/0085-fs-ntfs3-Change-posix_acl_equiv_mode-to-posix_acl_up.patch b/SOURCES/0085-fs-ntfs3-Change-posix_acl_equiv_mode-to-posix_acl_up.patch new file mode 100644 index 0000000..686d0a5 --- /dev/null +++ b/SOURCES/0085-fs-ntfs3-Change-posix_acl_equiv_mode-to-posix_acl_up.patch @@ -0,0 +1,49 @@ +From ba77237ef880320105e681b93d596b2fb13860f9 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Wed, 22 Sep 2021 18:46:14 +0300 +Subject: [Backport ba77237ef880] src: Change posix_acl_equiv_mode to + posix_acl_update_mode + +Right now ntfs3 uses posix_acl_equiv_mode instead of +posix_acl_update_mode like all other fs. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 15 ++++----------- + 1 file changed, 4 insertions(+), 11 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 70f2f9eb6b1eb3e7bebc3703762abfb7e42d7be1..59ec5e61a2390511a633c39b4e8ae3b01a2b9840 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -559,22 +559,15 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + if (acl) { + umode_t mode = inode->i_mode; + +- err = posix_acl_equiv_mode(acl, &mode); +- if (err < 0) +- return err; ++ err = posix_acl_update_mode(mnt_userns, inode, &mode, ++ &acl); ++ if (err) ++ goto out; + + if (inode->i_mode != mode) { + inode->i_mode = mode; + mark_inode_dirty(inode); + } +- +- if (!err) { +- /* +- * ACL can be exactly represented in the +- * traditional file mode permission bits. +- */ +- acl = NULL; +- } + } + name = XATTR_NAME_POSIX_ACL_ACCESS; + name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1; +-- +2.31.1 + diff --git a/SOURCES/0086-fs-ntfs3-Refactoring-lock-in-ntfs_init_acl.patch b/SOURCES/0086-fs-ntfs3-Refactoring-lock-in-ntfs_init_acl.patch new file mode 100644 index 0000000..83f0c9d --- /dev/null +++ b/SOURCES/0086-fs-ntfs3-Refactoring-lock-in-ntfs_init_acl.patch @@ -0,0 +1,89 @@ +From 66019837a5563e1f80a4228b7d110d8c52bfdd8b Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Wed, 22 Sep 2021 18:50:58 +0300 +Subject: [Backport 66019837a556] src: Refactoring lock in ntfs_init_acl + +This is possible because of moving lock into ntfs_create_inode. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 55 ++++++++++++------------------------------------ + 1 file changed, 14 insertions(+), 41 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 59ec5e61a2390511a633c39b4e8ae3b01a2b9840..83bbee277e12ec795dde29cf279b7eb96af961bc 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -693,54 +693,27 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct posix_acl *default_acl, *acl; + int err; + +- /* +- * TODO: Refactoring lock. +- * ni_lock(dir) ... -> posix_acl_create(dir,...) -> ntfs_get_acl -> ni_lock(dir) +- */ +- inode->i_default_acl = NULL; +- +- default_acl = ntfs_get_acl_ex(mnt_userns, dir, ACL_TYPE_DEFAULT, 1); +- +- if (!default_acl || default_acl == ERR_PTR(-EOPNOTSUPP)) { +- inode->i_mode &= ~current_umask(); +- err = 0; +- goto out; +- } +- +- if (IS_ERR(default_acl)) { +- err = PTR_ERR(default_acl); +- goto out; +- } +- +- acl = default_acl; +- err = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); +- if (err < 0) +- goto out1; +- if (!err) { +- posix_acl_release(acl); +- acl = NULL; +- } +- +- if (!S_ISDIR(inode->i_mode)) { +- posix_acl_release(default_acl); +- default_acl = NULL; +- } ++ err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); ++ if (err) ++ return err; + +- if (default_acl) ++ if (default_acl) { + err = ntfs_set_acl_ex(mnt_userns, inode, default_acl, + ACL_TYPE_DEFAULT, 1); ++ posix_acl_release(default_acl); ++ } else { ++ inode->i_default_acl = NULL; ++ } + + if (!acl) + inode->i_acl = NULL; +- else if (!err) +- err = ntfs_set_acl_ex(mnt_userns, inode, acl, ACL_TYPE_ACCESS, +- 1); +- +- posix_acl_release(acl); +-out1: +- posix_acl_release(default_acl); ++ else { ++ if (!err) ++ err = ntfs_set_acl_ex(mnt_userns, inode, acl, ++ ACL_TYPE_ACCESS, 1); ++ posix_acl_release(acl); ++ } + +-out: + return err; + } + #endif +-- +2.31.1 + diff --git a/SOURCES/0087-fs-ntfs3-Reject-mount-if-boot-s-cluster-size-media-s.patch b/SOURCES/0087-fs-ntfs3-Reject-mount-if-boot-s-cluster-size-media-s.patch new file mode 100644 index 0000000..da87317 --- /dev/null +++ b/SOURCES/0087-fs-ntfs3-Reject-mount-if-boot-s-cluster-size-media-s.patch @@ -0,0 +1,44 @@ +From 09f7c338da7818fd33af401d855b895550e7c170 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 28 Sep 2021 19:19:17 +0300 +Subject: [Backport 09f7c338da78] src: Reject mount if boot's cluster size + < media sector size + +If we continue to work in this case, then we can corrupt fs. +Fixes: 82cae269cfa9 ("src: Add initialization of super block"). + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/super.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/src/super.c b/src/super.c +index 800897777eb03855f702e8acf15b39d02c1c2fbf..e9b6a37345769700f682809832d84d8dfc7b9414 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -763,9 +763,20 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + sbi->mft.lbo = mlcn << sbi->cluster_bits; + sbi->mft.lbo2 = mlcn2 << sbi->cluster_bits; + +- if (sbi->cluster_size < sbi->sector_size) ++ /* Compare boot's cluster and sector. */ ++ if (sbi->cluster_size < boot_sector_size) + goto out; + ++ /* Compare boot's cluster and media sector. */ ++ if (sbi->cluster_size < sector_size) { ++ /* No way to use ntfs_get_block in this case. */ ++ ntfs_err( ++ sb, ++ "Failed to mount 'cause NTFS's cluster size (%u) is less than media sector size (%u)", ++ sbi->cluster_size, sector_size); ++ goto out; ++ } ++ + sbi->cluster_mask = sbi->cluster_size - 1; + sbi->cluster_mask_inv = ~(u64)sbi->cluster_mask; + sbi->record_size = record_size = boot->record_size < 0 +-- +2.31.1 + diff --git a/SOURCES/0088-fs-ntfs3-Refactoring-of-ntfs_init_from_boot.patch b/SOURCES/0088-fs-ntfs3-Refactoring-of-ntfs_init_from_boot.patch new file mode 100644 index 0000000..14a9d76 --- /dev/null +++ b/SOURCES/0088-fs-ntfs3-Refactoring-of-ntfs_init_from_boot.patch @@ -0,0 +1,86 @@ +From dbf59e2a33d2cb5ae0534523fd3d6d8bda808be8 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 28 Sep 2021 19:19:49 +0300 +Subject: [Backport dbf59e2a33d2] src: Refactoring of ntfs_init_from_boot + +Remove ntfs_sb_info members sector_size and sector_bits. +Print details why mount failed. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/ntfs_fs.h | 2 -- + src/super.c | 19 +++++++++---------- + 2 files changed, 9 insertions(+), 12 deletions(-) + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 6731b5d9e2d8efc742ce2200528e81eb22cbfa6e..38b7c1a9dc5209badca36b6a7cb8a2b38845b2e9 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -211,10 +211,8 @@ struct ntfs_sb_info { + u32 blocks_per_cluster; // cluster_size / sb->s_blocksize + + u32 record_size; +- u32 sector_size; + u32 index_size; + +- u8 sector_bits; + u8 cluster_bits; + u8 record_bits; + +diff --git a/src/super.c b/src/super.c +index e9b6a37345769700f682809832d84d8dfc7b9414..705d8b4f4894fbcc63f018e41437a54d6a0e1e6f 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -682,7 +682,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + struct ntfs_sb_info *sbi = sb->s_fs_info; + int err; + u32 mb, gb, boot_sector_size, sct_per_clst, record_size; +- u64 sectors, clusters, fs_size, mlcn, mlcn2; ++ u64 sectors, clusters, mlcn, mlcn2; + struct NTFS_BOOT *boot; + struct buffer_head *bh; + struct MFT_REC *rec; +@@ -740,20 +740,20 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + goto out; + } + +- sbi->sector_size = boot_sector_size; +- sbi->sector_bits = blksize_bits(boot_sector_size); +- fs_size = (sectors + 1) << sbi->sector_bits; ++ sbi->volume.size = sectors * boot_sector_size; + +- gb = format_size_gb(fs_size, &mb); ++ gb = format_size_gb(sbi->volume.size + boot_sector_size, &mb); + + /* + * - Volume formatted and mounted with the same sector size. + * - Volume formatted 4K and mounted as 512. + * - Volume formatted 512 and mounted as 4K. + */ +- if (sbi->sector_size != sector_size) { +- ntfs_warn(sb, +- "Different NTFS' sector size and media sector size"); ++ if (boot_sector_size != sector_size) { ++ ntfs_warn( ++ sb, ++ "Different NTFS' sector size (%u) and media sector size (%u)", ++ boot_sector_size, sector_size); + dev_size += sector_size - 1; + } + +@@ -800,10 +800,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, + : (u32)boot->index_size << sbi->cluster_bits; + + sbi->volume.ser_num = le64_to_cpu(boot->serial_num); +- sbi->volume.size = sectors << sbi->sector_bits; + + /* Warning if RAW volume. */ +- if (dev_size < fs_size) { ++ if (dev_size < sbi->volume.size + boot_sector_size) { + u32 mb0, gb0; + + gb0 = format_size_gb(dev_size, &mb0); +-- +2.31.1 + diff --git a/SOURCES/0089-fs-ntfs3-Check-for-NULL-if-ATTR_EA_INFO-is-incorrect.patch b/SOURCES/0089-fs-ntfs3-Check-for-NULL-if-ATTR_EA_INFO-is-incorrect.patch new file mode 100644 index 0000000..0480760 --- /dev/null +++ b/SOURCES/0089-fs-ntfs3-Check-for-NULL-if-ATTR_EA_INFO-is-incorrect.patch @@ -0,0 +1,34 @@ +From 35afb70dcfe4eb445060dd955e5b67d962869ce5 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Wed, 29 Sep 2021 19:29:52 +0300 +Subject: [Backport 35afb70dcfe4] src: Check for NULL if ATTR_EA_INFO is + incorrect + +This can be reason for reported panic +https://lore.kernel.org/ntfs3/f9de5807-2311-7374-afb0-bc5dffb522c0@gmail.com/ +Fixes: 4342306f0f0d ("src: Add file operations and implementation") + +Reported-by: Mohammad Rasim +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/frecord.c b/src/frecord.c +index 9a53f809576d0b795ebb6ed2834792a60be463b0..007602badd9000ce190dd5a0ad13e0618aa27e83 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -3080,7 +3080,9 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup, + const struct EA_INFO *info; + + info = resident_data_ex(attr, sizeof(struct EA_INFO)); +- dup->ea_size = info->size_pack; ++ /* If ATTR_EA_INFO exists 'info' can't be NULL. */ ++ if (info) ++ dup->ea_size = info->size_pack; + } + } + +-- +2.31.1 + diff --git a/SOURCES/0090-fs-ntfs3-Use-available-posix_acl_release-instead-of-.patch b/SOURCES/0090-fs-ntfs3-Use-available-posix_acl_release-instead-of-.patch new file mode 100644 index 0000000..1c46bc4 --- /dev/null +++ b/SOURCES/0090-fs-ntfs3-Use-available-posix_acl_release-instead-of-.patch @@ -0,0 +1,52 @@ +From b1e0c55a409955aba2d5a151f3f3a8bda8e45193 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 24 Sep 2021 17:47:04 +0300 +Subject: [Backport b1e0c55a4099] src: Use available posix_acl_release + instead of ntfs_posix_acl_release + +We don't need to maintain ntfs_posix_acl_release. + +Reviewed-by: Kari Argillander +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 83bbee277e12ec795dde29cf279b7eb96af961bc..253a07d9aa7b359fcb0296e859a57b0dc5492c30 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -475,12 +475,6 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + } + + #ifdef CONFIG_NTFS3_FS_POSIX_ACL +-static inline void ntfs_posix_acl_release(struct posix_acl *acl) +-{ +- if (acl && refcount_dec_and_test(&acl->a_refcount)) +- kfree(acl); +-} +- + static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + struct inode *inode, int type, + int locked) +@@ -641,7 +635,7 @@ static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns, + return -ENODATA; + + err = posix_acl_to_xattr(mnt_userns, acl, buffer, size); +- ntfs_posix_acl_release(acl); ++ posix_acl_release(acl); + + return err; + } +@@ -678,7 +672,7 @@ static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns, + err = ntfs_set_acl(mnt_userns, inode, acl, type); + + release_and_out: +- ntfs_posix_acl_release(acl); ++ posix_acl_release(acl); + return err; + } + +-- +2.31.1 + diff --git a/SOURCES/0091-fs-ntfs3-Remove-locked-argument-in-ntfs_set_ea.patch b/SOURCES/0091-fs-ntfs3-Remove-locked-argument-in-ntfs_set_ea.patch new file mode 100644 index 0000000..7c68d95 --- /dev/null +++ b/SOURCES/0091-fs-ntfs3-Remove-locked-argument-in-ntfs_set_ea.patch @@ -0,0 +1,139 @@ +From d81e06be921f90d5f1bada59d4549ca6f1bedc61 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 24 Sep 2021 17:50:22 +0300 +Subject: [Backport d81e06be921f] src: Remove locked argument in + ntfs_set_ea + +We always need to lock now, because locks became smaller +(see d562e901f25d +"src: Move ni_lock_dir and ni_unlock into ntfs_create_inode"). + +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 29 ++++++++++++++--------------- + 1 file changed, 14 insertions(+), 15 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 253a07d9aa7b359fcb0296e859a57b0dc5492c30..29f571b53083ea86d3091834749b8d87dec9d69c 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -257,7 +257,7 @@ static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len, + + static noinline int ntfs_set_ea(struct inode *inode, const char *name, + size_t name_len, const void *value, +- size_t val_size, int flags, int locked) ++ size_t val_size, int flags) + { + struct ntfs_inode *ni = ntfs_i(inode); + struct ntfs_sb_info *sbi = ni->mi.sbi; +@@ -276,8 +276,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + u64 new_sz; + void *p; + +- if (!locked) +- ni_lock(ni); ++ ni_lock(ni); + + run_init(&ea_run); + +@@ -465,8 +464,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + mark_inode_dirty(&ni->vfs_inode); + + out: +- if (!locked) +- ni_unlock(ni); ++ ni_unlock(ni); + + run_close(&ea_run); + kfree(ea_all); +@@ -537,7 +535,7 @@ struct posix_acl *ntfs_get_acl(struct inode *inode, int type) + + static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + struct inode *inode, struct posix_acl *acl, +- int type, int locked) ++ int type) + { + const char *name; + size_t size, name_len; +@@ -594,7 +592,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + flags = 0; + } + +- err = ntfs_set_ea(inode, name, name_len, value, size, flags, locked); ++ err = ntfs_set_ea(inode, name, name_len, value, size, flags); + if (err == -ENODATA && !size) + err = 0; /* Removing non existed xattr. */ + if (!err) +@@ -612,7 +610,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct posix_acl *acl, int type) + { +- return ntfs_set_acl_ex(mnt_userns, inode, acl, type, 0); ++ return ntfs_set_acl_ex(mnt_userns, inode, acl, type); + } + + static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns, +@@ -693,7 +691,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + + if (default_acl) { + err = ntfs_set_acl_ex(mnt_userns, inode, default_acl, +- ACL_TYPE_DEFAULT, 1); ++ ACL_TYPE_DEFAULT); + posix_acl_release(default_acl); + } else { + inode->i_default_acl = NULL; +@@ -704,7 +702,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + else { + if (!err) + err = ntfs_set_acl_ex(mnt_userns, inode, acl, +- ACL_TYPE_ACCESS, 1); ++ ACL_TYPE_ACCESS); + posix_acl_release(acl); + } + +@@ -988,7 +986,7 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + } + #endif + /* Deal with NTFS extended attribute. */ +- err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0); ++ err = ntfs_set_ea(inode, name, name_len, value, size, flags); + + out: + return err; +@@ -1004,28 +1002,29 @@ int ntfs_save_wsl_perm(struct inode *inode) + int err; + __le32 value; + ++ /* TODO: refactor this, so we don't lock 4 times in ntfs_set_ea */ + value = cpu_to_le32(i_uid_read(inode)); + err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value, +- sizeof(value), 0, 0); ++ sizeof(value), 0); + if (err) + goto out; + + value = cpu_to_le32(i_gid_read(inode)); + err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value, +- sizeof(value), 0, 0); ++ sizeof(value), 0); + if (err) + goto out; + + value = cpu_to_le32(inode->i_mode); + err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value, +- sizeof(value), 0, 0); ++ sizeof(value), 0); + if (err) + goto out; + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + value = cpu_to_le32(inode->i_rdev); + err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value, +- sizeof(value), 0, 0); ++ sizeof(value), 0); + if (err) + goto out; + } +-- +2.31.1 + diff --git a/SOURCES/0092-fs-ntfs3-Refactoring-of-ntfs_set_ea.patch b/SOURCES/0092-fs-ntfs3-Refactoring-of-ntfs_set_ea.patch new file mode 100644 index 0000000..9a2a95c --- /dev/null +++ b/SOURCES/0092-fs-ntfs3-Refactoring-of-ntfs_set_ea.patch @@ -0,0 +1,94 @@ +From cff32466bf851bf29cd491d8a3cbeb4dc4a36ab6 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 24 Sep 2021 17:53:56 +0300 +Subject: [Backport cff32466bf85] src: Refactoring of ntfs_set_ea + +Make code more readable. +Don't try to read zero bytes. +Add warning when size of exteneded attribute exceeds limit. + +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 31 +++++++++++++++++-------------- + 1 file changed, 17 insertions(+), 14 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index 29f571b53083ea86d3091834749b8d87dec9d69c..cdc01877227a509398c9c8b203382ac2f9858a5e 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -75,6 +75,7 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + size_t add_bytes, const struct EA_INFO **info) + { + int err; ++ struct ntfs_sb_info *sbi = ni->mi.sbi; + struct ATTR_LIST_ENTRY *le = NULL; + struct ATTRIB *attr_info, *attr_ea; + void *ea_p; +@@ -99,10 +100,10 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + + /* Check Ea limit. */ + size = le32_to_cpu((*info)->size); +- if (size > ni->mi.sbi->ea_max_size) ++ if (size > sbi->ea_max_size) + return -EFBIG; + +- if (attr_size(attr_ea) > ni->mi.sbi->ea_max_size) ++ if (attr_size(attr_ea) > sbi->ea_max_size) + return -EFBIG; + + /* Allocate memory for packed Ea. */ +@@ -110,15 +111,16 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea, + if (!ea_p) + return -ENOMEM; + +- if (attr_ea->non_res) { ++ if (!size) { ++ ; ++ } else if (attr_ea->non_res) { + struct runs_tree run; + + run_init(&run); + + err = attr_load_runs(attr_ea, ni, &run, NULL); + if (!err) +- err = ntfs_read_run_nb(ni->mi.sbi, &run, 0, ea_p, size, +- NULL); ++ err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL); + run_close(&run); + + if (err) +@@ -366,21 +368,22 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name, + new_ea->name[name_len] = 0; + memcpy(new_ea->name + name_len + 1, value, val_size); + new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea); +- +- /* Should fit into 16 bits. */ +- if (new_pack > 0xffff) { +- err = -EFBIG; // -EINVAL? +- goto out; +- } + ea_info.size_pack = cpu_to_le16(new_pack); +- + /* New size of ATTR_EA. */ + size += add; +- if (size > sbi->ea_max_size) { ++ ea_info.size = cpu_to_le32(size); ++ ++ /* ++ * 1. Check ea_info.size_pack for overflow. ++ * 2. New attibute size must fit value from $AttrDef ++ */ ++ if (new_pack > 0xffff || size > sbi->ea_max_size) { ++ ntfs_inode_warn( ++ inode, ++ "The size of extended attributes must not exceed 64KiB"); + err = -EFBIG; // -EINVAL? + goto out; + } +- ea_info.size = cpu_to_le32(size); + + update_ea: + +-- +2.31.1 + diff --git a/SOURCES/0093-fs-ntfs3-Forbid-FALLOC_FL_PUNCH_HOLE-for-normal-file.patch b/SOURCES/0093-fs-ntfs3-Forbid-FALLOC_FL_PUNCH_HOLE-for-normal-file.patch new file mode 100644 index 0000000..ccb8baa --- /dev/null +++ b/SOURCES/0093-fs-ntfs3-Forbid-FALLOC_FL_PUNCH_HOLE-for-normal-file.patch @@ -0,0 +1,39 @@ +From 8241fffae7c8bab5cec5fc8bcaceccd03079e3aa Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 28 Sep 2021 20:04:10 +0300 +Subject: [Backport 8241fffae7c8] src: Forbid FALLOC_FL_PUNCH_HOLE for + normal files + +FALLOC_FL_PUNCH_HOLE isn't allowed with normal files. +Filesystem must remember info about hole, but for normal file +we can only zero it and forget. + +Fixes: 4342306f0f0d ("src: Add file operations and implementation") +Now xfstests generic/016 generic/021 generic/022 pass. + +Signed-off-by: Konstantin Komarov +--- + src/file.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/file.c b/src/file.c +index 5fb3508e5422cc6654381897d6fdb4a204a543a4..43b1451bff539576cd03c0f624708bf65b959270 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -587,8 +587,11 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len) + truncate_pagecache(inode, vbo_down); + + if (!is_sparsed(ni) && !is_compressed(ni)) { +- /* Normal file. */ +- err = ntfs_zero_range(inode, vbo, end); ++ /* ++ * Normal file, can't make hole. ++ * TODO: Try to find way to save info about hole. ++ */ ++ err = -EOPNOTSUPP; + goto out; + } + +-- +2.31.1 + diff --git a/SOURCES/0094-fs-ntfs3-Remove-unnecessary-functions.patch b/SOURCES/0094-fs-ntfs3-Remove-unnecessary-functions.patch new file mode 100644 index 0000000..01baa03 --- /dev/null +++ b/SOURCES/0094-fs-ntfs3-Remove-unnecessary-functions.patch @@ -0,0 +1,135 @@ +From 95dd8b2c1ed00c76aaf41b552041c90724749a53 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 5 Oct 2021 17:22:29 +0300 +Subject: [Backport 95dd8b2c1ed0] src: Remove unnecessary functions + +We don't need ntfs_xattr_get_acl and ntfs_xattr_set_acl. +There are ntfs_get_acl_ex and ntfs_set_acl_ex. + +Signed-off-by: Konstantin Komarov +--- + src/xattr.c | 94 ------------------------------------------------ + 1 file changed, 94 deletions(-) + +diff --git a/src/xattr.c b/src/xattr.c +index cdc01877227a509398c9c8b203382ac2f9858a5e..2143099cffdf3a5377d964bd36463ea8f4af9c87 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -616,67 +616,6 @@ int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + return ntfs_set_acl_ex(mnt_userns, inode, acl, type); + } + +-static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns, +- struct inode *inode, int type, void *buffer, +- size_t size) +-{ +- struct posix_acl *acl; +- int err; +- +- if (!(inode->i_sb->s_flags & SB_POSIXACL)) { +- ntfs_inode_warn(inode, "add mount option \"acl\" to use acl"); +- return -EOPNOTSUPP; +- } +- +- acl = ntfs_get_acl(inode, type); +- if (IS_ERR(acl)) +- return PTR_ERR(acl); +- +- if (!acl) +- return -ENODATA; +- +- err = posix_acl_to_xattr(mnt_userns, acl, buffer, size); +- posix_acl_release(acl); +- +- return err; +-} +- +-static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns, +- struct inode *inode, int type, const void *value, +- size_t size) +-{ +- struct posix_acl *acl; +- int err; +- +- if (!(inode->i_sb->s_flags & SB_POSIXACL)) { +- ntfs_inode_warn(inode, "add mount option \"acl\" to use acl"); +- return -EOPNOTSUPP; +- } +- +- if (!inode_owner_or_capable(mnt_userns, inode)) +- return -EPERM; +- +- if (!value) { +- acl = NULL; +- } else { +- acl = posix_acl_from_xattr(mnt_userns, value, size); +- if (IS_ERR(acl)) +- return PTR_ERR(acl); +- +- if (acl) { +- err = posix_acl_valid(mnt_userns, acl); +- if (err) +- goto release_and_out; +- } +- } +- +- err = ntfs_set_acl(mnt_userns, inode, acl, type); +- +-release_and_out: +- posix_acl_release(acl); +- return err; +-} +- + /* + * ntfs_init_acl - Initialize the ACLs of a new inode. + * +@@ -843,23 +782,6 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de, + goto out; + } + +-#ifdef CONFIG_NTFS3_FS_POSIX_ACL +- if ((name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 && +- !memcmp(name, XATTR_NAME_POSIX_ACL_ACCESS, +- sizeof(XATTR_NAME_POSIX_ACL_ACCESS))) || +- (name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 && +- !memcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, +- sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)))) { +- /* TODO: init_user_ns? */ +- err = ntfs_xattr_get_acl( +- &init_user_ns, inode, +- name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 +- ? ACL_TYPE_ACCESS +- : ACL_TYPE_DEFAULT, +- buffer, size); +- goto out; +- } +-#endif + /* Deal with NTFS extended attribute. */ + err = ntfs_get_ea(inode, name, name_len, buffer, size, NULL); + +@@ -972,22 +894,6 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler, + goto out; + } + +-#ifdef CONFIG_NTFS3_FS_POSIX_ACL +- if ((name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 && +- !memcmp(name, XATTR_NAME_POSIX_ACL_ACCESS, +- sizeof(XATTR_NAME_POSIX_ACL_ACCESS))) || +- (name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 && +- !memcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, +- sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)))) { +- err = ntfs_xattr_set_acl( +- mnt_userns, inode, +- name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 +- ? ACL_TYPE_ACCESS +- : ACL_TYPE_DEFAULT, +- value, size); +- goto out; +- } +-#endif + /* Deal with NTFS extended attribute. */ + err = ntfs_set_ea(inode, name, name_len, value, size, flags); + +-- +2.31.1 + diff --git a/SOURCES/0095-fs-ntfs3-Keep-prealloc-for-all-types-of-files.patch b/SOURCES/0095-fs-ntfs3-Keep-prealloc-for-all-types-of-files.patch new file mode 100644 index 0000000..f0862a7 --- /dev/null +++ b/SOURCES/0095-fs-ntfs3-Keep-prealloc-for-all-types-of-files.patch @@ -0,0 +1,49 @@ +From ce46ae0c3e31400dc89d1e4620a812647cab9c72 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Fri, 1 Oct 2021 18:48:49 +0300 +Subject: [Backport ce46ae0c3e31] src: Keep prealloc for all types of + files + +Before we haven't kept prealloc for sparse files because we thought that +it will speed up create / write operations. +It lead to situation, when user reserved some space for sparse file, +filled volume, and wasn't able to write in reserved file. +With this commit we keep prealloc. +Now xfstest generic/274 pass. +Fixes: be71b5cba2e6 ("src: Add attrib operations") + +Signed-off-by: Konstantin Komarov +--- + src/attrib.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/src/attrib.c b/src/attrib.c +index 8a00fa978f5f9c3d496bd333bf1ace72d9194f37..e8c00dda42adbbff372908fdcb3d7d6814567cdf 100644 +--- a/src/attrib.c ++++ b/src/attrib.c +@@ -447,11 +447,8 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + again_1: + align = sbi->cluster_size; + +- if (is_ext) { ++ if (is_ext) + align <<= attr_b->nres.c_unit; +- if (is_attr_sparsed(attr_b)) +- keep_prealloc = false; +- } + + old_valid = le64_to_cpu(attr_b->nres.valid_size); + old_size = le64_to_cpu(attr_b->nres.data_size); +@@ -461,9 +458,6 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, + new_alloc = (new_size + align - 1) & ~(u64)(align - 1); + new_alen = new_alloc >> cluster_bits; + +- if (keep_prealloc && is_ext) +- keep_prealloc = false; +- + if (keep_prealloc && new_size < old_size) { + attr_b->nres.data_size = cpu_to_le64(new_size); + mi_b->dirty = true; +-- +2.31.1 + diff --git a/SOURCES/0096-fs-ntfs3-Fix-memory-leak-if-fill_super-failed.patch b/SOURCES/0096-fs-ntfs3-Fix-memory-leak-if-fill_super-failed.patch new file mode 100644 index 0000000..69bbf50 --- /dev/null +++ b/SOURCES/0096-fs-ntfs3-Fix-memory-leak-if-fill_super-failed.patch @@ -0,0 +1,310 @@ +From 9b75450d6c580100611743fa7e690ea3cb47cd4a Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 28 Sep 2021 19:00:30 +0300 +Subject: [Backport 9b75450d6c58] src: Fix memory leak if fill_super + failed + +In ntfs_init_fs_context we allocate memory in fc->s_fs_info. +In case of failed mount we must free it in ntfs_fill_super. +We can't do it in ntfs_fs_free, because ntfs_fs_free called +with fc->s_fs_info == NULL. +fc->s_fs_info became NULL in sget_fc. + +Signed-off-by: Konstantin Komarov +--- + src/super.c | 90 ++++++++++++++++++++++++++++++------------------ + 1 file changed, 56 insertions(+), 34 deletions(-) + +diff --git a/src/super.c b/src/super.c +index 705d8b4f4894fbcc63f018e41437a54d6a0e1e6f..d41d76979e121fd9e836ef0072b122ab6de3735b 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -908,7 +908,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + if (IS_ERR(sbi->options->nls)) { + sbi->options->nls = NULL; + errorf(fc, "Cannot load nls %s", sbi->options->nls_name); +- return -EINVAL; ++ err = -EINVAL; ++ goto out; + } + + rq = bdev_get_queue(bdev); +@@ -922,7 +923,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512, + bdev->bd_inode->i_size); + if (err) +- return err; ++ goto out; + + /* + * Load $Volume. This should be done before $LogFile +@@ -933,7 +934,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_VOLUME); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $Volume."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + ni = ntfs_i(inode); +@@ -954,19 +956,19 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + } else { + /* Should we break mounting here? */ + //err = -EINVAL; +- //goto out; ++ //goto put_inode_out; + } + + attr = ni_find_attr(ni, attr, NULL, ATTR_VOL_INFO, NULL, 0, NULL, NULL); + if (!attr || is_attr_ext(attr)) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + + info = resident_data_ex(attr, SIZEOF_ATTRIBUTE_VOLUME_INFO); + if (!info) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + + sbi->volume.major_ver = info->major_ver; +@@ -980,7 +982,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_MIRROR); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $MFTMirr."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + sbi->mft.recs_mirr = +@@ -994,14 +997,15 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_LOGFILE); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load \x24LogFile."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + ni = ntfs_i(inode); + + err = ntfs_loadlog_and_replay(ni, sbi); + if (err) +- goto out; ++ goto put_inode_out; + + iput(inode); + +@@ -1009,14 +1013,16 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + if (!sb_rdonly(sb)) { + ntfs_warn(sb, + "failed to replay log file. Can't mount rw!"); +- return -EINVAL; ++ err = -EINVAL; ++ goto out; + } + } else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) { + if (!sb_rdonly(sb) && !sbi->options->force) { + ntfs_warn( + sb, + "volume is dirty and \"force\" flag is not set!"); +- return -EINVAL; ++ err = -EINVAL; ++ goto out; + } + } + +@@ -1027,7 +1033,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_MFT); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $MFT."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + ni = ntfs_i(inode); +@@ -1038,11 +1045,11 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + err = wnd_init(&sbi->mft.bitmap, sb, tt); + if (err) +- goto out; ++ goto put_inode_out; + + err = ni_load_all_mi(ni); + if (err) +- goto out; ++ goto put_inode_out; + + sbi->mft.ni = ni; + +@@ -1052,7 +1059,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $BadClus."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + ni = ntfs_i(inode); +@@ -1075,13 +1083,14 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_BITMAP); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $Bitmap."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + #ifndef CONFIG_NTFS3_64BIT_CLUSTER + if (inode->i_size >> 32) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + #endif + +@@ -1089,21 +1098,21 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + tt = sbi->used.bitmap.nbits; + if (inode->i_size < bitmap_size(tt)) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + + /* Not necessary. */ + sbi->used.bitmap.set_tail = true; + err = wnd_init(&sbi->used.bitmap, sb, tt); + if (err) +- goto out; ++ goto put_inode_out; + + iput(inode); + + /* Compute the MFT zone. */ + err = ntfs_refresh_zone(sbi); + if (err) +- return err; ++ goto out; + + /* Load $AttrDef. */ + ref.low = cpu_to_le32(MFT_REC_ATTR); +@@ -1111,18 +1120,19 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_ATTRDEF); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $AttrDef -> %d", err); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + if (inode->i_size < sizeof(struct ATTR_DEF_ENTRY)) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + bytes = inode->i_size; + sbi->def_table = t = kmalloc(bytes, GFP_NOFS); + if (!t) { + err = -ENOMEM; +- goto out; ++ goto put_inode_out; + } + + for (done = idx = 0; done < bytes; done += PAGE_SIZE, idx++) { +@@ -1131,7 +1141,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + if (IS_ERR(page)) { + err = PTR_ERR(page); +- goto out; ++ goto put_inode_out; + } + memcpy(Add2Ptr(t, done), page_address(page), + min(PAGE_SIZE, tail)); +@@ -1139,7 +1149,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + if (!idx && ATTR_STD != t->type) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + } + +@@ -1173,12 +1183,13 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_UPCASE); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load $UpCase."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + if (inode->i_size != 0x10000 * sizeof(short)) { + err = -EINVAL; +- goto out; ++ goto put_inode_out; + } + + for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) { +@@ -1188,7 +1199,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + + if (IS_ERR(page)) { + err = PTR_ERR(page); +- goto out; ++ goto put_inode_out; + } + + src = page_address(page); +@@ -1214,7 +1225,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + /* Load $Secure. */ + err = ntfs_security_init(sbi); + if (err) +- return err; ++ goto out; + + /* Load $Extend. */ + err = ntfs_extend_init(sbi); +@@ -1239,19 +1250,30 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) + inode = ntfs_iget5(sb, &ref, &NAME_ROOT); + if (IS_ERR(inode)) { + ntfs_err(sb, "Failed to load root."); +- return PTR_ERR(inode); ++ err = PTR_ERR(inode); ++ goto out; + } + + sb->s_root = d_make_root(inode); +- if (!sb->s_root) +- return -ENOMEM; ++ if (!sb->s_root) { ++ err = -ENOMEM; ++ goto put_inode_out; ++ } + + fc->fs_private = NULL; +- fc->s_fs_info = NULL; + + return 0; +-out: ++ ++put_inode_out: + iput(inode); ++out: ++ /* ++ * Free resources here. ++ * ntfs_fs_free will be called with fc->s_fs_info = NULL ++ */ ++ put_ntfs(sbi); ++ sb->s_fs_info = NULL; ++ + return err; + } + +-- +2.31.1 + diff --git a/SOURCES/0097-fs-ntfs3-Rework-ntfs_utf16_to_nls.patch b/SOURCES/0097-fs-ntfs3-Rework-ntfs_utf16_to_nls.patch new file mode 100644 index 0000000..34e12d3 --- /dev/null +++ b/SOURCES/0097-fs-ntfs3-Rework-ntfs_utf16_to_nls.patch @@ -0,0 +1,92 @@ +From 2c69078851b3d7495eb191158d0bcb9c91a6d148 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Mon, 4 Oct 2021 18:22:45 +0300 +Subject: [Backport 2c69078851b3] src: Rework ntfs_utf16_to_nls + +Now ntfs_utf16_to_nls takes length as one of arguments. +If length of symlink > 255, then we tried to convert +length of symlink +- some random number. +Now 255 symbols limit was removed. + +Signed-off-by: Konstantin Komarov +--- + src/dir.c | 19 ++++++++----------- + src/ntfs_fs.h | 2 +- + 2 files changed, 9 insertions(+), 12 deletions(-) + +diff --git a/src/dir.c b/src/dir.c +index 785e72d4392e152ef6833721e887ed9ef056094b..fb438d6040409838389ba7454645034ab2fa7a08 100644 +--- a/src/dir.c ++++ b/src/dir.c +@@ -15,11 +15,10 @@ + #include "ntfs_fs.h" + + /* Convert little endian UTF-16 to NLS string. */ +-int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, ++int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len, + u8 *buf, int buf_len) + { +- int ret, uni_len, warn; +- const __le16 *ip; ++ int ret, warn; + u8 *op; + struct nls_table *nls = sbi->options->nls; + +@@ -27,18 +26,16 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + + if (!nls) { + /* UTF-16 -> UTF-8 */ +- ret = utf16s_to_utf8s((wchar_t *)uni->name, uni->len, +- UTF16_LITTLE_ENDIAN, buf, buf_len); ++ ret = utf16s_to_utf8s(name, len, UTF16_LITTLE_ENDIAN, buf, ++ buf_len); + buf[ret] = '\0'; + return ret; + } + +- ip = uni->name; + op = buf; +- uni_len = uni->len; + warn = 0; + +- while (uni_len--) { ++ while (len--) { + u16 ec; + int charlen; + char dump[5]; +@@ -49,7 +46,7 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, + break; + } + +- ec = le16_to_cpu(*ip++); ++ ec = le16_to_cpu(*name++); + charlen = nls->uni2char(ec, op, buf_len); + + if (charlen > 0) { +@@ -304,8 +301,8 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni, + if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN)) + return 0; + +- name_len = ntfs_utf16_to_nls(sbi, (struct le_str *)&fname->name_len, +- name, PATH_MAX); ++ name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name, ++ PATH_MAX); + if (name_len <= 0) { + ntfs_warn(sbi->sb, "failed to convert name for inode %lx.", + ino); +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 38b7c1a9dc5209badca36b6a7cb8a2b38845b2e9..9277b552f2578ee8e94784afe9772614b02748a0 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -475,7 +475,7 @@ bool are_bits_set(const ulong *map, size_t bit, size_t nbits); + size_t get_set_bits_ex(const ulong *map, size_t bit, size_t nbits); + + /* Globals from dir.c */ +-int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni, ++int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len, + u8 *buf, int buf_len); + int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len, + struct cpu_str *uni, u32 max_ulen, +-- +2.31.1 + diff --git a/SOURCES/0098-fs-ntfs3-Refactor-ntfs_readlink_hlp.patch b/SOURCES/0098-fs-ntfs3-Refactor-ntfs_readlink_hlp.patch new file mode 100644 index 0000000..e5d1ca1 --- /dev/null +++ b/SOURCES/0098-fs-ntfs3-Refactor-ntfs_readlink_hlp.patch @@ -0,0 +1,178 @@ +From 4dbe8e4413d70ac4f163592d69eef74d7824783a Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Mon, 4 Oct 2021 18:59:55 +0300 +Subject: [Backport 4dbe8e4413d7] src: Refactor ntfs_readlink_hlp + +Rename some variables. +Returned err by default is EINVAL. + +Signed-off-by: Konstantin Komarov +--- + src/inode.c | 91 +++++++++++++++++++++++------------------------- + 1 file changed, 43 insertions(+), 48 deletions(-) + +diff --git a/src/inode.c b/src/inode.c +index 7dd162f6a7e26435b16c08b5efed6e283b975f54..d618b0573533c043acd2fadc59a20e9ce0c772cb 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1763,15 +1763,15 @@ void ntfs_evict_inode(struct inode *inode) + static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + int buflen) + { +- int i, err = 0; ++ int i, err = -EINVAL; + struct ntfs_inode *ni = ntfs_i(inode); + struct super_block *sb = inode->i_sb; + struct ntfs_sb_info *sbi = sb->s_fs_info; +- u64 i_size = inode->i_size; +- u16 nlen = 0; ++ u64 size; ++ u16 ulen = 0; + void *to_free = NULL; + struct REPARSE_DATA_BUFFER *rp; +- struct le_str *uni; ++ const __le16 *uname; + struct ATTRIB *attr; + + /* Reparse data present. Try to parse it. */ +@@ -1780,68 +1780,64 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + + *buffer = 0; + +- /* Read into temporal buffer. */ +- if (i_size > sbi->reparse.max_size || i_size <= sizeof(u32)) { +- err = -EINVAL; +- goto out; +- } +- + attr = ni_find_attr(ni, NULL, NULL, ATTR_REPARSE, NULL, 0, NULL, NULL); +- if (!attr) { +- err = -EINVAL; ++ if (!attr) + goto out; +- } + + if (!attr->non_res) { +- rp = resident_data_ex(attr, i_size); +- if (!rp) { +- err = -EINVAL; ++ rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER)); ++ if (!rp) + goto out; +- } ++ size = le32_to_cpu(attr->res.data_size); + } else { +- rp = kmalloc(i_size, GFP_NOFS); ++ size = le64_to_cpu(attr->nres.data_size); ++ rp = NULL; ++ } ++ ++ if (size > sbi->reparse.max_size || size <= sizeof(u32)) ++ goto out; ++ ++ if (!rp) { ++ rp = kmalloc(size, GFP_NOFS); + if (!rp) { + err = -ENOMEM; + goto out; + } + to_free = rp; +- err = ntfs_read_run_nb(sbi, &ni->file.run, 0, rp, i_size, NULL); ++ /* Read into temporal buffer. */ ++ err = ntfs_read_run_nb(sbi, &ni->file.run, 0, rp, size, NULL); + if (err) + goto out; + } + +- err = -EINVAL; +- + /* Microsoft Tag. */ + switch (rp->ReparseTag) { + case IO_REPARSE_TAG_MOUNT_POINT: + /* Mount points and junctions. */ + /* Can we use 'Rp->MountPointReparseBuffer.PrintNameLength'? */ +- if (i_size <= offsetof(struct REPARSE_DATA_BUFFER, +- MountPointReparseBuffer.PathBuffer)) ++ if (size <= offsetof(struct REPARSE_DATA_BUFFER, ++ MountPointReparseBuffer.PathBuffer)) + goto out; +- uni = Add2Ptr(rp, +- offsetof(struct REPARSE_DATA_BUFFER, +- MountPointReparseBuffer.PathBuffer) + +- le16_to_cpu(rp->MountPointReparseBuffer +- .PrintNameOffset) - +- 2); +- nlen = le16_to_cpu(rp->MountPointReparseBuffer.PrintNameLength); ++ uname = Add2Ptr(rp, ++ offsetof(struct REPARSE_DATA_BUFFER, ++ MountPointReparseBuffer.PathBuffer) + ++ le16_to_cpu(rp->MountPointReparseBuffer ++ .PrintNameOffset)); ++ ulen = le16_to_cpu(rp->MountPointReparseBuffer.PrintNameLength); + break; + + case IO_REPARSE_TAG_SYMLINK: + /* FolderSymbolicLink */ + /* Can we use 'Rp->SymbolicLinkReparseBuffer.PrintNameLength'? */ +- if (i_size <= offsetof(struct REPARSE_DATA_BUFFER, +- SymbolicLinkReparseBuffer.PathBuffer)) ++ if (size <= offsetof(struct REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer.PathBuffer)) + goto out; +- uni = Add2Ptr(rp, +- offsetof(struct REPARSE_DATA_BUFFER, +- SymbolicLinkReparseBuffer.PathBuffer) + +- le16_to_cpu(rp->SymbolicLinkReparseBuffer +- .PrintNameOffset) - +- 2); +- nlen = le16_to_cpu( ++ uname = Add2Ptr( ++ rp, offsetof(struct REPARSE_DATA_BUFFER, ++ SymbolicLinkReparseBuffer.PathBuffer) + ++ le16_to_cpu(rp->SymbolicLinkReparseBuffer ++ .PrintNameOffset)); ++ ulen = le16_to_cpu( + rp->SymbolicLinkReparseBuffer.PrintNameLength); + break; + +@@ -1873,29 +1869,28 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer, + goto out; + } + if (!IsReparseTagNameSurrogate(rp->ReparseTag) || +- i_size <= sizeof(struct REPARSE_POINT)) { ++ size <= sizeof(struct REPARSE_POINT)) { + goto out; + } + + /* Users tag. */ +- uni = Add2Ptr(rp, sizeof(struct REPARSE_POINT) - 2); +- nlen = le16_to_cpu(rp->ReparseDataLength) - ++ uname = Add2Ptr(rp, sizeof(struct REPARSE_POINT)); ++ ulen = le16_to_cpu(rp->ReparseDataLength) - + sizeof(struct REPARSE_POINT); + } + + /* Convert nlen from bytes to UNICODE chars. */ +- nlen >>= 1; ++ ulen >>= 1; + + /* Check that name is available. */ +- if (!nlen || &uni->name[nlen] > (__le16 *)Add2Ptr(rp, i_size)) ++ if (!ulen || uname + ulen > (__le16 *)Add2Ptr(rp, size)) + goto out; + + /* If name is already zero terminated then truncate it now. */ +- if (!uni->name[nlen - 1]) +- nlen -= 1; +- uni->len = nlen; ++ if (!uname[ulen - 1]) ++ ulen -= 1; + +- err = ntfs_utf16_to_nls(sbi, uni, buffer, buflen); ++ err = ntfs_utf16_to_nls(sbi, uname, ulen, buffer, buflen); + + if (err < 0) + goto out; +-- +2.31.1 + diff --git a/SOURCES/0099-fs-ntfs3-Refactor-ntfs_create_inode.patch b/SOURCES/0099-fs-ntfs3-Refactor-ntfs_create_inode.patch new file mode 100644 index 0000000..6a4e779 --- /dev/null +++ b/SOURCES/0099-fs-ntfs3-Refactor-ntfs_create_inode.patch @@ -0,0 +1,57 @@ +From 14a981193e409d8ca3f89768cfbd7a9a516d9927 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Mon, 4 Oct 2021 18:52:24 +0300 +Subject: [Backport 14a981193e40] src: Refactor ntfs_create_inode + +Set size for symlink, so we don't need to calculate it on the fly. + +Signed-off-by: Konstantin Komarov +--- + src/inode.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/src/inode.c b/src/inode.c +index d618b0573533c043acd2fadc59a20e9ce0c772cb..bdebbbd53e766f7435640e55c1551be0fadccd19 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1488,7 +1488,10 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + asize = ALIGN(SIZEOF_RESIDENT + nsize, 8); + t16 = PtrOffset(rec, attr); + +- /* 0x78 - the size of EA + EAINFO to store WSL */ ++ /* ++ * Below function 'ntfs_save_wsl_perm' requires 0x78 bytes. ++ * It is good idea to keep extened attributes resident. ++ */ + if (asize + t16 + 0x78 + 8 > sbi->record_size) { + CLST alen; + CLST clst = bytes_to_cluster(sbi, nsize); +@@ -1523,14 +1526,14 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + } + + asize = SIZEOF_NONRESIDENT + ALIGN(err, 8); +- inode->i_size = nsize; + } else { + attr->res.data_off = SIZEOF_RESIDENT_LE; + attr->res.data_size = cpu_to_le32(nsize); + memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), rp, nsize); +- inode->i_size = nsize; + nsize = 0; + } ++ /* Size of symlink equals the length of input string. */ ++ inode->i_size = size; + + attr->size = cpu_to_le32(asize); + +@@ -1567,6 +1570,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + inode->i_op = &ntfs_link_inode_operations; + inode->i_fop = NULL; + inode->i_mapping->a_ops = &ntfs_aops; ++ inode->i_size = size; ++ inode_nohighmem(inode); + } else if (S_ISREG(mode)) { + inode->i_op = &ntfs_file_inode_operations; + inode->i_fop = &ntfs_file_operations; +-- +2.31.1 + diff --git a/SOURCES/0100-fs-ntfs3-Refactor-ni_parse_reparse.patch b/SOURCES/0100-fs-ntfs3-Refactor-ni_parse_reparse.patch new file mode 100644 index 0000000..f086ea8 --- /dev/null +++ b/SOURCES/0100-fs-ntfs3-Refactor-ni_parse_reparse.patch @@ -0,0 +1,65 @@ +From cd4c76ff807c1afeed89f1b7e311760c0e296349 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 5 Oct 2021 19:08:08 +0300 +Subject: [Backport cd4c76ff807c] src: Refactor ni_parse_reparse + +Change argument from void* to struct REPARSE_DATA_BUFFER* +We copy data to buffer, so we can read it later in ntfs_read_mft. + +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 9 +++++---- + src/ntfs_fs.h | 2 +- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/frecord.c b/src/frecord.c +index 007602badd9000ce190dd5a0ad13e0618aa27e83..ecb965e4afd075e2e38be7dfda7ad71f3ff1e150 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -1710,18 +1710,16 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa) + /* + * ni_parse_reparse + * +- * Buffer is at least 24 bytes. ++ * buffer - memory for reparse buffer header + */ + enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, +- void *buffer) ++ struct REPARSE_DATA_BUFFER *buffer) + { + const struct REPARSE_DATA_BUFFER *rp = NULL; + u8 bits; + u16 len; + typeof(rp->CompressReparseBuffer) *cmpr; + +- static_assert(sizeof(struct REPARSE_DATA_BUFFER) <= 24); +- + /* Try to estimate reparse point. */ + if (!attr->non_res) { + rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER)); +@@ -1807,6 +1805,9 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, + return REPARSE_NONE; + } + ++ if (buffer != rp) ++ memcpy(buffer, rp, sizeof(struct REPARSE_DATA_BUFFER)); ++ + /* Looks like normal symlink. */ + return REPARSE_LINK; + } +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 9277b552f2578ee8e94784afe9772614b02748a0..e95d93c683ed9852a1b941c3ac8585af0b925f7f 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -547,7 +547,7 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type, + struct ATTR_LIST_ENTRY **entry); + int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa); + enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr, +- void *buffer); ++ struct REPARSE_DATA_BUFFER *buffer); + int ni_write_inode(struct inode *inode, int sync, const char *hint); + #define _ni_write_inode(i, w) ni_write_inode(i, w, __func__) + int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo, +-- +2.31.1 + diff --git a/SOURCES/0101-fs-ntfs3-Refactor-ntfs_read_mft.patch b/SOURCES/0101-fs-ntfs3-Refactor-ntfs_read_mft.patch new file mode 100644 index 0000000..e1f8d59 --- /dev/null +++ b/SOURCES/0101-fs-ntfs3-Refactor-ntfs_read_mft.patch @@ -0,0 +1,63 @@ +From 22b05f1ac0332cb47701649206997d8d9a1a7f24 Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Tue, 5 Oct 2021 19:33:26 +0300 +Subject: [Backport 22b05f1ac033] src: Refactor ntfs_read_mft + +Don't save size of attribute reparse point as size of symlink. + +Signed-off-by: Konstantin Komarov +--- + src/inode.c | 22 ++++++++-------------- + 1 file changed, 8 insertions(+), 14 deletions(-) + +diff --git a/src/inode.c b/src/inode.c +index bdebbbd53e766f7435640e55c1551be0fadccd19..859951d785cb2f4380c85b1d799f66f008da7d45 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -222,9 +222,6 @@ static struct inode *ntfs_read_mft(struct inode *inode, + if (!attr->non_res) { + ni->i_valid = inode->i_size = rsize; + inode_set_bytes(inode, rsize); +- t32 = asize; +- } else { +- t32 = le16_to_cpu(attr->nres.run_off); + } + + mode = S_IFREG | (0777 & sbi->options->fs_fmask_inv); +@@ -313,17 +310,14 @@ static struct inode *ntfs_read_mft(struct inode *inode, + rp_fa = ni_parse_reparse(ni, attr, &rp); + switch (rp_fa) { + case REPARSE_LINK: +- if (!attr->non_res) { +- inode->i_size = rsize; +- inode_set_bytes(inode, rsize); +- t32 = asize; +- } else { +- inode->i_size = +- le64_to_cpu(attr->nres.data_size); +- t32 = le16_to_cpu(attr->nres.run_off); +- } ++ /* ++ * Normal symlink. ++ * Assume one unicode symbol == one utf8. ++ */ ++ inode->i_size = le16_to_cpu(rp.SymbolicLinkReparseBuffer ++ .PrintNameLength) / ++ sizeof(u16); + +- /* Looks like normal symlink. */ + ni->i_valid = inode->i_size; + + /* Clear directory bit. */ +@@ -420,7 +414,7 @@ static struct inode *ntfs_read_mft(struct inode *inode, + ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; + inode->i_op = &ntfs_link_inode_operations; + inode->i_fop = NULL; +- inode_nohighmem(inode); // ?? ++ inode_nohighmem(inode); + } else if (S_ISREG(mode)) { + ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY; + inode->i_op = &ntfs_file_inode_operations; +-- +2.31.1 + diff --git a/SOURCES/0102-fs-ntfs3-Check-for-NULL-pointers-in-ni_try_remove_at.patch b/SOURCES/0102-fs-ntfs3-Check-for-NULL-pointers-in-ni_try_remove_at.patch new file mode 100644 index 0000000..c842c39 --- /dev/null +++ b/SOURCES/0102-fs-ntfs3-Check-for-NULL-pointers-in-ni_try_remove_at.patch @@ -0,0 +1,72 @@ +From 8607954cf255329d1c6dfc073ff1508b7585573c Mon Sep 17 00:00:00 2001 +From: Konstantin Komarov +Date: Mon, 11 Oct 2021 19:43:29 +0300 +Subject: [Backport 8607954cf255] src: Check for NULL pointers in + ni_try_remove_attr_list + +Check for potential NULL pointers. +Print error message if found. +Thread, that leads to this commit: +https://lore.kernel.org/ntfs3/227c13e3-5a22-0cba-41eb-fcaf41940711@paragon-software.com/ + +Reported-by: Mohammad Rasim +Signed-off-by: Konstantin Komarov +--- + src/frecord.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +diff --git a/src/frecord.c b/src/frecord.c +index ecb965e4afd075e2e38be7dfda7ad71f3ff1e150..6f47a9c17f896c62e355db7a6948075a88247378 100644 +--- a/src/frecord.c ++++ b/src/frecord.c +@@ -705,18 +705,35 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + continue; + + mi = ni_find_mi(ni, ino_get(&le->ref)); ++ if (!mi) { ++ /* Should never happened, 'cause already checked. */ ++ goto bad; ++ } + + attr = mi_find_attr(mi, NULL, le->type, le_name(le), + le->name_len, &le->id); ++ if (!attr) { ++ /* Should never happened, 'cause already checked. */ ++ goto bad; ++ } + asize = le32_to_cpu(attr->size); + + /* Insert into primary record. */ + attr_ins = mi_insert_attr(&ni->mi, le->type, le_name(le), + le->name_len, asize, + le16_to_cpu(attr->name_off)); +- id = attr_ins->id; ++ if (!attr_ins) { ++ /* ++ * Internal error. ++ * Either no space in primary record (already checked). ++ * Either tried to insert another ++ * non indexed attribute (logic error). ++ */ ++ goto bad; ++ } + + /* Copy all except id. */ ++ id = attr_ins->id; + memcpy(attr_ins, attr, asize); + attr_ins->id = id; + +@@ -732,6 +749,10 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni) + ni->attr_list.dirty = false; + + return 0; ++bad: ++ ntfs_inode_err(&ni->vfs_inode, "Internal error"); ++ make_bad_inode(&ni->vfs_inode); ++ return -EINVAL; + } + + /* +-- +2.31.1 + diff --git a/SOURCES/0103-merge.patch b/SOURCES/0103-merge.patch new file mode 100644 index 0000000..af8417a --- /dev/null +++ b/SOURCES/0103-merge.patch @@ -0,0 +1,70 @@ +From 86a44e9067c95083d5dbf5a140e3f4560e5af1ca +From: Linus Torvalds +Date: Fri, 15 Oct 2021 09:58:11 -0400 +Subject: [Backport 86a44e9067c9] Merge tag 'ntfs3_for_5.15' of git://github.com/Paragon-Software-Group/linux-ntfs3 + +Pull ntfs3 fixes from Konstantin Komarov: + "Use the new api for mounting as requested by Christoph. + + Also fixed: + + - some memory leaks and panic + + - xfstests (tested on x86_64) generic/016 generic/021 generic/022 + generic/041 generic/274 generic/423 + + - some typos, wrong returned error codes, dead code, etc" + +* tag 'ntfs3_for_5.15' of git://github.com/Paragon-Software-Group/linux-ntfs3: (70 commits) + src: Check for NULL pointers in ni_try_remove_attr_list + src: Refactor ntfs_read_mft + src: Refactor ni_parse_reparse + src: Refactor ntfs_create_inode + src: Refactor ntfs_readlink_hlp + src: Rework ntfs_utf16_to_nls + src: Fix memory leak if fill_super failed + src: Keep prealloc for all types of files + src: Remove unnecessary functions + src: Forbid FALLOC_FL_PUNCH_HOLE for normal files + src: Refactoring of ntfs_set_ea + src: Remove locked argument in ntfs_set_ea + src: Use available posix_acl_release instead of ntfs_posix_acl_release + src: Check for NULL if ATTR_EA_INFO is incorrect + src: Refactoring of ntfs_init_from_boot + src: Reject mount if boot's cluster size < media sector size + src: Refactoring lock in ntfs_init_acl + src: Change posix_acl_equiv_mode to posix_acl_update_mode + src: Pass flags to ntfs_set_ea in ntfs_set_acl_ex + src: Refactor ntfs_get_acl_ex for better readability + ... + +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index e95d93c683ed9852a1b941c3ac8585af0b925f7f..8aaec7e0804efaa4b66727bf0d7360f68abba8c0 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -836,7 +836,7 @@ int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2, + + /* globals from xattr.c */ + #ifdef CONFIG_NTFS3_FS_POSIX_ACL +-struct posix_acl *ntfs_get_acl(struct inode *inode, int type); ++struct posix_acl *ntfs_get_acl(struct inode *inode, int type, bool rcu); + int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + struct posix_acl *acl, int type); + int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, +diff --git a/src/xattr.c b/src/xattr.c +index 2143099cffdf3a5377d964bd36463ea8f4af9c87..afd0ddad826ff49f661ce758bcbecec46921fd1c 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -530,8 +530,11 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + /* + * ntfs_get_acl - inode_operations::get_acl + */ +-struct posix_acl *ntfs_get_acl(struct inode *inode, int type) ++struct posix_acl *ntfs_get_acl(struct inode *inode, int type, bool rcu) + { ++ if (rcu) ++ return ERR_PTR(-ECHILD); ++ + /* TODO: init_user_ns? */ + return ntfs_get_acl_ex(&init_user_ns, inode, type, 0); + } diff --git a/SOURCES/9001-Remove-idmap-mount-support.patch b/SOURCES/9001-Remove-idmap-mount-support.patch new file mode 100644 index 0000000..14f039a --- /dev/null +++ b/SOURCES/9001-Remove-idmap-mount-support.patch @@ -0,0 +1,387 @@ +From 8a80b8c6acb10a4659ed0566d7de3ab883ed5a31 Mon Sep 17 00:00:00 2001 +From: Peter Georg +Date: Mon, 18 Oct 2021 16:44:57 +0200 +Subject: [PATCH 9001/9005] Remove idmap mount support + +--- + src/file.c | 12 ++++++------ + src/inode.c | 6 +++--- + src/namei.c | 18 +++++++++--------- + src/ntfs_fs.h | 14 +++++++------- + src/super.c | 7 +++---- + src/xattr.c | 31 +++++++++++++++---------------- + 6 files changed, 43 insertions(+), 45 deletions(-) + +diff --git a/src/file.c b/src/file.c +index 43b1451..ee9a472 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -69,7 +69,7 @@ static long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg) + /* + * ntfs_getattr - inode_operations::getattr + */ +-int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, ++int ntfs_getattr(const struct path *path, + struct kstat *stat, u32 request_mask, u32 flags) + { + struct inode *inode = d_inode(path->dentry); +@@ -83,7 +83,7 @@ int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, + + stat->attributes_mask |= STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED; + +- generic_fillattr(mnt_userns, inode, stat); ++ generic_fillattr(inode, stat); + + stat->result_mask |= STATX_BTIME; + stat->btime = ni->i_crtime; +@@ -728,7 +728,7 @@ out: + /* + * ntfs3_setattr - inode_operations::setattr + */ +-int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ++int ntfs3_setattr(struct dentry *dentry, + struct iattr *attr) + { + struct super_block *sb = dentry->d_sb; +@@ -747,7 +747,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + ia_valid = attr->ia_valid; + } + +- err = setattr_prepare(mnt_userns, dentry, attr); ++ err = setattr_prepare(dentry, attr); + if (err) + goto out; + +@@ -772,10 +772,10 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + ni->ni_flags |= NI_FLAG_UPDATE_PARENT; + } + +- setattr_copy(mnt_userns, inode, attr); ++ setattr_copy(inode, attr); + + if (mode != inode->i_mode) { +- err = ntfs_acl_chmod(mnt_userns, inode); ++ err = ntfs_acl_chmod(inode); + if (err) + goto out; + +diff --git a/src/inode.c b/src/inode.c +index 859951d..69eef0f 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -1163,7 +1163,7 @@ out: + return ERR_PTR(err); + } + +-struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, ++struct inode *ntfs_create_inode( + struct inode *dir, struct dentry *dentry, + const struct cpu_str *uni, umode_t mode, + dev_t dev, const char *symname, u32 size, +@@ -1280,7 +1280,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + goto out3; + } + inode = &ni->vfs_inode; +- inode_init_owner(mnt_userns, inode, dir, mode); ++ inode_init_owner(inode, dir, mode); + mode = inode->i_mode; + + inode->i_atime = inode->i_mtime = inode->i_ctime = ni->i_crtime = +@@ -1579,7 +1579,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, + + #ifdef CONFIG_NTFS3_FS_POSIX_ACL + if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) { +- err = ntfs_init_acl(mnt_userns, inode, dir); ++ err = ntfs_init_acl(inode, dir); + if (err) + goto out7; + } else +diff --git a/src/namei.c b/src/namei.c +index bc74121..7e346b8 100644 +--- a/src/namei.c ++++ b/src/namei.c +@@ -92,12 +92,12 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry, + /* + * ntfs_create - inode_operations::create + */ +-static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, ++static int ntfs_create(struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) + { + struct inode *inode; + +- inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFREG | mode, ++ inode = ntfs_create_inode(dir, dentry, NULL, S_IFREG | mode, + 0, NULL, 0, NULL); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +@@ -108,12 +108,12 @@ static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir, + * + * inode_operations::mknod + */ +-static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir, ++static int ntfs_mknod(struct inode *dir, + struct dentry *dentry, umode_t mode, dev_t rdev) + { + struct inode *inode; + +- inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, mode, rdev, ++ inode = ntfs_create_inode(dir, dentry, NULL, mode, rdev, + NULL, 0, NULL); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +@@ -181,13 +181,13 @@ static int ntfs_unlink(struct inode *dir, struct dentry *dentry) + /* + * ntfs_symlink - inode_operations::symlink + */ +-static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, ++static int ntfs_symlink(struct inode *dir, + struct dentry *dentry, const char *symname) + { + u32 size = strlen(symname); + struct inode *inode; + +- inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFLNK | 0777, ++ inode = ntfs_create_inode(dir, dentry, NULL, S_IFLNK | 0777, + 0, symname, size, NULL); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +@@ -196,12 +196,12 @@ static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir, + /* + * ntfs_mkdir- inode_operations::mkdir + */ +-static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir, ++static int ntfs_mkdir(struct inode *dir, + struct dentry *dentry, umode_t mode) + { + struct inode *inode; + +- inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFDIR | mode, ++ inode = ntfs_create_inode(dir, dentry, NULL, S_IFDIR | mode, + 0, NULL, 0, NULL); + + return IS_ERR(inode) ? PTR_ERR(inode) : 0; +@@ -227,7 +227,7 @@ static int ntfs_rmdir(struct inode *dir, struct dentry *dentry) + /* + * ntfs_rename - inode_operations::rename + */ +-static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *dir, ++static int ntfs_rename(struct inode *dir, + struct dentry *dentry, struct inode *new_dir, + struct dentry *new_dentry, u32 flags) + { +diff --git a/src/ntfs_fs.h b/src/ntfs_fs.h +index 8aaec7e..040fa56 100644 +--- a/src/ntfs_fs.h ++++ b/src/ntfs_fs.h +@@ -486,11 +486,11 @@ bool dir_is_empty(struct inode *dir); + extern const struct file_operations ntfs_dir_operations; + + /* Globals from file.c */ +-int ntfs_getattr(struct user_namespace *mnt_userns, const struct path *path, ++int ntfs_getattr(const struct path *path, + struct kstat *stat, u32 request_mask, u32 flags); + void ntfs_sparse_cluster(struct inode *inode, struct page *page0, CLST vcn, + CLST len); +-int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ++int ntfs3_setattr(struct dentry *dentry, + struct iattr *attr); + int ntfs_file_open(struct inode *inode, struct file *file); + int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, +@@ -695,7 +695,7 @@ int ntfs_sync_inode(struct inode *inode); + int ntfs_flush_inodes(struct super_block *sb, struct inode *i1, + struct inode *i2); + int inode_write_data(struct inode *inode, const void *data, size_t bytes); +-struct inode *ntfs_create_inode(struct user_namespace *mnt_userns, ++struct inode *ntfs_create_inode( + struct inode *dir, struct dentry *dentry, + const struct cpu_str *uni, umode_t mode, + dev_t dev, const char *symname, u32 size, +@@ -837,17 +837,17 @@ int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2, + /* globals from xattr.c */ + #ifdef CONFIG_NTFS3_FS_POSIX_ACL + struct posix_acl *ntfs_get_acl(struct inode *inode, int type, bool rcu); +-int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, ++int ntfs_set_acl(struct inode *inode, + struct posix_acl *acl, int type); +-int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, ++int ntfs_init_acl(struct inode *inode, + struct inode *dir); + #else + #define ntfs_get_acl NULL + #define ntfs_set_acl NULL + #endif + +-int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode); +-int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, ++int ntfs_acl_chmod(struct inode *inode); ++int ntfs_permission(struct inode *inode, + int mask); + ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size); + extern const struct xattr_handler *ntfs_xattr_handlers[]; +diff --git a/src/super.c b/src/super.c +index d41d769..97cfd3f 100644 +--- a/src/super.c ++++ b/src/super.c +@@ -511,12 +511,11 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root) + struct super_block *sb = root->d_sb; + struct ntfs_sb_info *sbi = sb->s_fs_info; + struct ntfs_mount_options *opts = sbi->options; +- struct user_namespace *user_ns = seq_user_ns(m); + + seq_printf(m, ",uid=%u", +- from_kuid_munged(user_ns, opts->fs_uid)); ++ from_kuid_munged(&init_user_ns, opts->fs_uid)); + seq_printf(m, ",gid=%u", +- from_kgid_munged(user_ns, opts->fs_gid)); ++ from_kgid_munged(&init_user_ns, opts->fs_gid)); + if (opts->fmask) + seq_printf(m, ",fmask=%04o", ~opts->fs_fmask_inv); + if (opts->dmask) +@@ -1435,7 +1434,7 @@ static struct file_system_type ntfs_fs_type = { + .init_fs_context = ntfs_init_fs_context, + .parameters = ntfs_fs_parameters, + .kill_sb = kill_block_super, +- .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, ++ .fs_flags = FS_REQUIRES_DEV, + }; + // clang-format on + +diff --git a/src/xattr.c b/src/xattr.c +index afd0dda..110fecb 100644 +--- a/src/xattr.c ++++ b/src/xattr.c +@@ -476,7 +476,7 @@ out: + } + + #ifdef CONFIG_NTFS3_FS_POSIX_ACL +-static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, ++static struct posix_acl *ntfs_get_acl_ex( + struct inode *inode, int type, + int locked) + { +@@ -512,7 +512,7 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns, + + /* Translate extended attribute to acl. */ + if (err >= 0) { +- acl = posix_acl_from_xattr(mnt_userns, buf, err); ++ acl = posix_acl_from_xattr(&init_user_ns, buf, err); + } else if (err == -ENODATA) { + acl = NULL; + } else { +@@ -536,10 +536,10 @@ struct posix_acl *ntfs_get_acl(struct inode *inode, int type, bool rcu) + return ERR_PTR(-ECHILD); + + /* TODO: init_user_ns? */ +- return ntfs_get_acl_ex(&init_user_ns, inode, type, 0); ++ return ntfs_get_acl_ex(inode, type, 0); + } + +-static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, ++static noinline int ntfs_set_acl_ex( + struct inode *inode, struct posix_acl *acl, + int type) + { +@@ -557,7 +557,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + if (acl) { + umode_t mode = inode->i_mode; + +- err = posix_acl_update_mode(mnt_userns, inode, &mode, ++ err = posix_acl_update_mode(inode, &mode, + &acl); + if (err) + goto out; +@@ -592,7 +592,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns, + value = kmalloc(size, GFP_NOFS); + if (!value) + return -ENOMEM; +- err = posix_acl_to_xattr(mnt_userns, acl, value, size); ++ err = posix_acl_to_xattr(&init_user_ns, acl, value, size); + if (err < 0) + goto out; + flags = 0; +@@ -613,10 +613,10 @@ out: + /* + * ntfs_set_acl - inode_operations::set_acl + */ +-int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, ++int ntfs_set_acl(struct inode *inode, + struct posix_acl *acl, int type) + { +- return ntfs_set_acl_ex(mnt_userns, inode, acl, type); ++ return ntfs_set_acl_ex(inode, acl, type); + } + + /* +@@ -624,7 +624,7 @@ int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode, + * + * Called from ntfs_create_inode(). + */ +-int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, ++int ntfs_init_acl(struct inode *inode, + struct inode *dir) + { + struct posix_acl *default_acl, *acl; +@@ -635,7 +635,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + return err; + + if (default_acl) { +- err = ntfs_set_acl_ex(mnt_userns, inode, default_acl, ++ err = ntfs_set_acl_ex(inode, default_acl, + ACL_TYPE_DEFAULT); + posix_acl_release(default_acl); + } else { +@@ -646,7 +646,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + inode->i_acl = NULL; + else { + if (!err) +- err = ntfs_set_acl_ex(mnt_userns, inode, acl, ++ err = ntfs_set_acl_ex(inode, acl, + ACL_TYPE_ACCESS); + posix_acl_release(acl); + } +@@ -658,7 +658,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode, + /* + * ntfs_acl_chmod - Helper for ntfs3_setattr(). + */ +-int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) ++int ntfs_acl_chmod(struct inode *inode) + { + struct super_block *sb = inode->i_sb; + +@@ -668,13 +668,13 @@ int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode) + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + +- return posix_acl_chmod(mnt_userns, inode, inode->i_mode); ++ return posix_acl_chmod(inode, inode->i_mode); + } + + /* + * ntfs_permission - inode_operations::permission + */ +-int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, ++int ntfs_permission(struct inode *inode, + int mask) + { + if (ntfs_sb(inode->i_sb)->options->noacsrules) { +@@ -682,7 +682,7 @@ int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode, + return 0; + } + +- return generic_permission(mnt_userns, inode, mask); ++ return generic_permission(inode, mask); + } + + /* +@@ -796,7 +796,6 @@ out: + * ntfs_setxattr - inode_operations::setxattr + */ + static noinline int ntfs_setxattr(const struct xattr_handler *handler, +- struct user_namespace *mnt_userns, + struct dentry *de, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) +-- +2.31.1 + diff --git a/SOURCES/9002-Compat-Replace-copy_page_from_iter_atomic-by-iov_ite.patch b/SOURCES/9002-Compat-Replace-copy_page_from_iter_atomic-by-iov_ite.patch new file mode 100644 index 0000000..553bec8 --- /dev/null +++ b/SOURCES/9002-Compat-Replace-copy_page_from_iter_atomic-by-iov_ite.patch @@ -0,0 +1,31 @@ +From 3705e1ac8e76edc6edff937b6f9622eb9fd1938b Mon Sep 17 00:00:00 2001 +From: Peter Georg +Date: Wed, 22 Sep 2021 22:31:23 +0200 +Subject: [PATCH 9002/9005] Compat: Replace copy_page_from_iter_atomic by + iov_iter_copy_from_user_atomic + +--- + src/file.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/file.c b/src/file.c +index ee9a472..d89afcd 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -1028,10 +1028,11 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from) + size_t cp, tail = PAGE_SIZE - off; + + page = pages[ip]; +- cp = copy_page_from_iter_atomic(page, off, +- min(tail, bytes), from); ++ cp = iov_iter_copy_from_user_atomic(page, from, off, ++ min(tail, bytes)); + flush_dcache_page(page); + ++ iov_iter_advance(from, cp); + copied += cp; + bytes -= cp; + if (!bytes || !cp) +-- +2.31.1 + diff --git a/SOURCES/9003-Compat-Replace-BIO_MAX_VECS-by-BIO_MAX_PAGES.patch b/SOURCES/9003-Compat-Replace-BIO_MAX_VECS-by-BIO_MAX_PAGES.patch new file mode 100644 index 0000000..39b2727 --- /dev/null +++ b/SOURCES/9003-Compat-Replace-BIO_MAX_VECS-by-BIO_MAX_PAGES.patch @@ -0,0 +1,25 @@ +From 61b202bf20ac931e408f48eb55a6c552105f75ae Mon Sep 17 00:00:00 2001 +From: Peter Georg +Date: Wed, 22 Sep 2021 22:31:49 +0200 +Subject: [PATCH 9003/9005] Compat: Replace BIO_MAX_VECS by BIO_MAX_PAGES + +--- + src/fsntfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/fsntfs.c b/src/fsntfs.c +index 4de9acb..6d02b2c 100644 +--- a/src/fsntfs.c ++++ b/src/fsntfs.c +@@ -1599,7 +1599,7 @@ int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run) + lbo = (u64)lcn << cluster_bits; + len = (u64)clen << cluster_bits; + new_bio: +- new = ntfs_alloc_bio(BIO_MAX_VECS); ++ new = ntfs_alloc_bio(BIO_MAX_PAGES); + if (!new) { + err = -ENOMEM; + break; +-- +2.31.1 + diff --git a/SOURCES/9004-Compat-Replace-readahead-by-readpages.patch b/SOURCES/9004-Compat-Replace-readahead-by-readpages.patch new file mode 100644 index 0000000..6a40172 --- /dev/null +++ b/SOURCES/9004-Compat-Replace-readahead-by-readpages.patch @@ -0,0 +1,75 @@ +From 4047e5d6ee42f45b206864a99360fae0f013c902 Mon Sep 17 00:00:00 2001 +From: Peter Georg +Date: Wed, 22 Sep 2021 22:32:09 +0200 +Subject: [PATCH 9004/9005] Compat: Replace readahead by readpages + +--- + src/inode.c | 29 +++++++++-------------------- + 1 file changed, 9 insertions(+), 20 deletions(-) + +diff --git a/src/inode.c b/src/inode.c +index 69eef0f..20b9aea 100644 +--- a/src/inode.c ++++ b/src/inode.c +@@ -704,34 +704,23 @@ static int ntfs_readpage(struct file *file, struct page *page) + return mpage_readpage(page, ntfs_get_block); + } + +-static void ntfs_readahead(struct readahead_control *rac) ++static int ntfs_readpages(struct file *file, struct address_space *mapping, ++ struct list_head *pages, unsigned int nr_pages) + { +- struct address_space *mapping = rac->mapping; + struct inode *inode = mapping->host; + struct ntfs_inode *ni = ntfs_i(inode); +- u64 valid; +- loff_t pos; + + if (is_resident(ni)) { +- /* No readahead for resident. */ +- return; ++ /* No readpages for resident. */ ++ return 0; + } + + if (is_compressed(ni)) { +- /* No readahead for compressed. */ +- return; +- } +- +- valid = ni->i_valid; +- pos = readahead_pos(rac); +- +- if (valid < i_size_read(inode) && pos <= valid && +- valid < pos + readahead_length(rac)) { +- /* Range cross 'valid'. Read it page by page. */ +- return; ++ /* No readpages for compressed. */ ++ return 0; + } + +- mpage_readahead(rac, ntfs_get_block); ++ return mpage_readpages(mapping, pages, nr_pages, ntfs_get_block); + } + + static int ntfs_get_block_direct_IO_R(struct inode *inode, sector_t iblock, +@@ -1943,7 +1932,7 @@ const struct inode_operations ntfs_link_inode_operations = { + + const struct address_space_operations ntfs_aops = { + .readpage = ntfs_readpage, +- .readahead = ntfs_readahead, ++ .readpages = ntfs_readpages, + .writepage = ntfs_writepage, + .writepages = ntfs_writepages, + .write_begin = ntfs_write_begin, +@@ -1955,6 +1944,6 @@ const struct address_space_operations ntfs_aops = { + + const struct address_space_operations ntfs_aops_cmpr = { + .readpage = ntfs_readpage, +- .readahead = ntfs_readahead, ++ .readpages = ntfs_readpages, + }; + // clang-format on +-- +2.31.1 + diff --git a/SOURCES/9005-Compat-Add-fiemap_prep-function.patch b/SOURCES/9005-Compat-Add-fiemap_prep-function.patch new file mode 100644 index 0000000..790dc96 --- /dev/null +++ b/SOURCES/9005-Compat-Add-fiemap_prep-function.patch @@ -0,0 +1,76 @@ +From 9d008240e684ea13b35b107589f354a266aca850 Mon Sep 17 00:00:00 2001 +From: Peter Georg +Date: Wed, 22 Sep 2021 22:32:42 +0200 +Subject: [PATCH 9005/9005] Compat: Add fiemap_prep function + +--- + src/compat.h | 43 +++++++++++++++++++++++++++++++++++++++++++ + src/file.c | 2 ++ + 2 files changed, 45 insertions(+) + create mode 100644 src/compat.h + +diff --git a/src/compat.h b/src/compat.h +new file mode 100644 +index 0000000..9862d76 +--- /dev/null ++++ b/src/compat.h +@@ -0,0 +1,43 @@ ++ /** ++ * fiemap_prep - check validity of requested flags for fiemap ++ * @inode: Inode to operate on ++ * @fieinfo: Fiemap context passed into ->fiemap ++ * @start: Start of the mapped range ++ * @len: Length of the mapped range, can be truncated by this function. ++ * @supported_flags: Set of fiemap flags that the file system understands ++ * ++ * This function must be called from each ->fiemap instance to validate the ++ * fiemap request against the file system parameters. ++ * ++ * Returns 0 on success, or a negative error on failure. ++ */ ++static int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo, ++ u64 start, u64 *len, u32 supported_flags) ++{ ++ u64 maxbytes = inode->i_sb->s_maxbytes; ++ u32 incompat_flags; ++ int ret = 0; ++ ++ if (*len == 0) ++ return -EINVAL; ++ if (start > maxbytes) ++ return -EFBIG; ++ ++ /* ++ * Shrink request scope to what the fs can actually handle. ++ */ ++ if (*len > maxbytes || (maxbytes - *len) < start) ++ *len = maxbytes - start; ++ ++ supported_flags |= FIEMAP_FLAG_SYNC; ++ supported_flags &= FIEMAP_FLAGS_COMPAT; ++ incompat_flags = fieinfo->fi_flags & ~supported_flags; ++ if (incompat_flags) { ++ fieinfo->fi_flags = incompat_flags; ++ return -EBADR; ++ } ++ ++ if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC) ++ ret = filemap_write_and_wait(inode->i_mapping); ++ return ret; ++} +diff --git a/src/file.c b/src/file.c +index d89afcd..d72800c 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -17,6 +17,8 @@ + #include "ntfs.h" + #include "ntfs_fs.h" + ++#include "compat.h" ++ + static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg) + { + struct fstrim_range __user *user_range; +-- +2.31.1 + diff --git a/SOURCES/9999-enable-ntfs3.patch b/SOURCES/9999-enable-ntfs3.patch new file mode 100644 index 0000000..a9e5483 --- /dev/null +++ b/SOURCES/9999-enable-ntfs3.patch @@ -0,0 +1,9 @@ +--- a/src/Makefile ++++ b/src/Makefile +@@ -11 +11 @@ +-obj-$(CONFIG_NTFS3_FS) += ntfs3.o ++obj-m += ntfs3.o +@@ -32 +32,2 @@ +-ntfs3-$(CONFIG_NTFS3_LZX_XPRESS) += $(addprefix lib/,\ ++ccflags-y += -DCONFIG_NTFS3_LZX_XPRESS ++ntfs3-y += $(addprefix lib/,\ diff --git a/SPECS/kmod-ntfs3.spec b/SPECS/kmod-ntfs3.spec index cbe8d67..1d5688b 100644 --- a/SPECS/kmod-ntfs3.spec +++ b/SPECS/kmod-ntfs3.spec @@ -1,5 +1,7 @@ %global pkg ntfs3 +%global driver_version 4.18.0-0.el8 + %global kernel_version 4.18.0-348.el8 %global _use_internal_dependency_generator 0 @@ -18,14 +20,123 @@ Name: kmod-%{pkg} -Version: 5.15 -Release: 4%{?dist} +Version: 4.18.0.0 +Release: 1%{?dist} Summary: NTFS filesystem support License: GPLv2 URL: https://www.kernel.org/ -Source0: %{pkg}-%{version}.tar.xz +Source0: %{pkg}-%{driver_version}.tar.xz +Patch1: 0001-fs-ntfs3-Add-headers-and-misc-files.patch +Patch2: 0002-fs-ntfs3-Add-initialization-of-super-block.patch +Patch3: 0003-fs-ntfs3-Add-bitmap.patch +Patch4: 0004-fs-ntfs3-Add-file-operations-and-implementation.patch +Patch5: 0005-fs-ntfs3-Add-attrib-operations.patch +Patch6: 0006-fs-ntfs3-Add-compression.patch +Patch7: 0007-fs-ntfs3-Add-NTFS-journal.patch +Patch8: 0008-fs-ntfs3-Add-Kconfig-Makefile-and-doc.patch +Patch9: 0009-fs-ntfs3-Fix-various-spelling-mistakes.patch +Patch10: 0010-fs-ntfs3-Use-linux-log2-is_power_of_2-function.patch +Patch11: 0011-fs-ntfs3-Add-ifndef-define-to-all-header-files.patch +Patch12: 0012-fs-ntfs3-Fix-integer-overflow-in-multiplication.patch +Patch13: 0013-fs-ntfs3-Remove-unused-variable-cnt-in-ntfs_security.patch +Patch14: 0014-fs-ntfs3-Fix-one-none-utf8-char-in-source-file.patch +Patch15: 0015-fs-ntfs3-Fix-fall-through-warnings-for-Clang.patch +Patch16: 0016-fs-ntfs3-Remove-unused-including-linux-version.h.patch +Patch17: 0017-fs-ntfs3-Restyle-comment-block-in-ni_parse_reparse.patch +Patch18: 0018-fs-ntfs3-Use-kernel-ALIGN-macros-over-driver-specifi.patch +Patch19: 0019-fs-ntfs3-Do-not-use-driver-own-alloc-wrappers.patch +Patch20: 0020-fs-ntfs3-Use-kcalloc-kmalloc_array-over-kzalloc-kmal.patch +Patch21: 0021-fs-ntfs3-add-checks-for-allocation-failure.patch +Patch22: 0022-fs-ntfs3-fix-an-error-code-in-ntfs_get_acl_ex.patch +Patch23: 0023-fs-ntfs3-Fix-error-code-in-indx_add_allocate.patch +Patch24: 0024-fs-ntfs3-Potential-NULL-dereference-in-hdr_find_spli.patch +Patch25: 0025-fs-ntfs3-Fix-error-handling-in-indx_insert_into_root.patch +Patch26: 0026-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch +Patch27: 0027-fs-ntfs3-Remove-fat-ioctl-s-from-ntfs3-driver-for-no.patch +Patch28: 0028-fs-ntfs3-Rework-file-operations.patch +Patch29: 0029-fs-ntfs3-Restyle-comments-to-better-align-with-kerne.patch +Patch30: 0030-fs-ntfs3-Fix-integer-overflow-in-ni_fiemap-with-fiem.patch +Patch31: 0031-fs-ntfs3-Remove-unnecessary-condition-checking-from-.patch +Patch32: 0032-fs-ntfs3-Remove-GPL-boilerplates-from-decompress-lib.patch +Patch33: 0033-fs-ntfs3-Change-how-module-init-info-messages-are-di.patch +Patch34: 0034-fs-ntfs3-Remove-unnecesarry-mount-option-noatime.patch +Patch35: 0035-fs-ntfs3-Remove-unnecesarry-remount-flag-handling.patch +Patch36: 0036-fs-ntfs3-Convert-mount-options-to-pointer-in-sbi.patch +Patch37: 0037-fs-ntfs3-Use-new-api-for-mounting.patch +Patch38: 0038-fs-ntfs3-Init-spi-more-in-init_fs_context-than-fill_.patch +Patch39: 0039-fs-ntfs3-Make-mount-option-nohidden-more-universal.patch +Patch40: 0040-fs-ntfs3-Add-iocharset-mount-option-as-alias-for-nls.patch +Patch41: 0041-fs-ntfs3-Rename-mount-option-no_acs_rules-no-acsrule.patch +Patch42: 0042-fs-ntfs3-Show-uid-gid-always-in-show_options.patch +Patch43: 0043-fs-ntfs3-Remove-redundant-initialization-of-variable.patch +Patch44: 0044-fs-ntfs3.-Add-forward-declarations-for-structs-to-de.patch +Patch45: 0045-fs-ntfs3-Add-missing-header-files-to-ntfs.h.patch +Patch46: 0046-fs-ntfs3-Add-missing-headers-and-forward-declaration.patch +Patch47: 0047-fs-ntfs3-Add-missing-header-and-guards-to-lib-header.patch +Patch48: 0048-fs-ntfs3-Change-right-headers-to-bitfunc.c.patch +Patch49: 0049-fs-ntfs3-Change-right-headers-to-upcase.c.patch +Patch50: 0050-fs-ntfs3-Change-right-headers-to-lznt.c.patch +Patch51: 0051-fs-ntfs3-Remove-unneeded-header-files-from-c-files.patch +Patch52: 0052-fs-ntfs3-Limit-binary-search-table-size.patch +Patch53: 0053-fs-ntfs3-Make-binary-search-to-search-smaller-chunks.patch +Patch54: 0054-fs-ntfs3-Always-use-binary-search-with-entry-search.patch +Patch55: 0055-fs-ntfs3-Remove-before-constant-in-ni_insert_residen.patch +Patch56: 0056-fs-ntfs3-Place-Comparisons-constant-right-side-of-th.patch +Patch57: 0057-fs-ntfs3-Remove-braces-from-single-statment-block.patch +Patch58: 0058-fs-ntfs3-Remove-tabs-before-spaces-from-comment.patch +Patch59: 0059-fs-ntfs3-Fix-ntfs_look_for_free_space-does-only-repo.patch +Patch60: 0060-fs-ntfs3-Remove-always-false-condition-check.patch +Patch61: 0061-fs-ntfs3-Use-clamp-max-macros-instead-of-comparisons.patch +Patch62: 0062-fs-ntfs3-Use-min-max-macros-instated-of-ternary-oper.patch +Patch63: 0063-fs-ntfs3-Fix-wrong-error-message-Logfile-UpCase.patch +Patch64: 0064-fs-ntfs3-Change-EINVAL-to-ENOMEM-when-d_make_root-fa.patch +Patch65: 0065-fs-ntfs3-Remove-impossible-fault-condition-in-fill_s.patch +Patch66: 0066-fs-ntfs3-Return-straight-without-goto-in-fill_super.patch +Patch67: 0067-fs-ntfs3-Remove-unnecessary-variable-loading-in-fill.patch +Patch68: 0068-fs-ntfs3-Use-sb-instead-of-sbi-sb-in-fill_super.patch +Patch69: 0069-fs-ntfs3-Remove-tmp-var-is_ro-in-ntfs_fill_super.patch +Patch70: 0070-fs-ntfs3-Remove-tmp-pointer-bd_inode-in-fill_super.patch +Patch71: 0071-fs-ntfs3-Remove-tmp-pointer-upcase-in-fill_super.patch +Patch72: 0072-fs-ntfs3-Initialize-pointer-before-use-place-in-fill.patch +Patch73: 0073-fs-ntfs3-Initiliaze-sb-blocksize-only-in-one-place-r.patch +Patch74: 0074-fs-ntfs3-Fix-a-memory-leak-on-object-opts.patch +Patch75: 0075-fs-ntfs3-Fix-insertion-of-attr-in-ni_ins_attr_ext.patch +Patch76: 0076-fs-ntfs3-Change-max-hardlinks-limit-to-4000.patch +Patch77: 0077-fs-ntfs3-Add-sync-flag-to-ntfs_sb_write_run-and-al_u.patch +Patch78: 0078-fs-ntfs3-Remove-a-useless-test-in-indx_find.patch +Patch79: 0079-fs-ntfs3-Remove-a-useless-shadowing-variable.patch +Patch80: 0080-fs-ntfs3-Remove-deprecated-mount-options-nls.patch +Patch81: 0081-fs-ntfs3-Fix-logical-error-in-ntfs_create_inode.patch +Patch82: 0082-fs-ntfs3-Move-ni_lock_dir-and-ni_unlock-into-ntfs_cr.patch +Patch83: 0083-fs-ntfs3-Refactor-ntfs_get_acl_ex-for-better-readabi.patch +Patch84: 0084-fs-ntfs3-Pass-flags-to-ntfs_set_ea-in-ntfs_set_acl_e.patch +Patch85: 0085-fs-ntfs3-Change-posix_acl_equiv_mode-to-posix_acl_up.patch +Patch86: 0086-fs-ntfs3-Refactoring-lock-in-ntfs_init_acl.patch +Patch87: 0087-fs-ntfs3-Reject-mount-if-boot-s-cluster-size-media-s.patch +Patch88: 0088-fs-ntfs3-Refactoring-of-ntfs_init_from_boot.patch +Patch89: 0089-fs-ntfs3-Check-for-NULL-if-ATTR_EA_INFO-is-incorrect.patch +Patch90: 0090-fs-ntfs3-Use-available-posix_acl_release-instead-of-.patch +Patch91: 0091-fs-ntfs3-Remove-locked-argument-in-ntfs_set_ea.patch +Patch92: 0092-fs-ntfs3-Refactoring-of-ntfs_set_ea.patch +Patch93: 0093-fs-ntfs3-Forbid-FALLOC_FL_PUNCH_HOLE-for-normal-file.patch +Patch94: 0094-fs-ntfs3-Remove-unnecessary-functions.patch +Patch95: 0095-fs-ntfs3-Keep-prealloc-for-all-types-of-files.patch +Patch96: 0096-fs-ntfs3-Fix-memory-leak-if-fill_super-failed.patch +Patch97: 0097-fs-ntfs3-Rework-ntfs_utf16_to_nls.patch +Patch98: 0098-fs-ntfs3-Refactor-ntfs_readlink_hlp.patch +Patch99: 0099-fs-ntfs3-Refactor-ntfs_create_inode.patch +Patch100: 0100-fs-ntfs3-Refactor-ni_parse_reparse.patch +Patch101: 0101-fs-ntfs3-Refactor-ntfs_read_mft.patch +Patch102: 0102-fs-ntfs3-Check-for-NULL-pointers-in-ni_try_remove_at.patch +Patch103: 0103-merge.patch +Patch9001: 9001-Remove-idmap-mount-support.patch +Patch9002: 9002-Compat-Replace-copy_page_from_iter_atomic-by-iov_ite.patch +Patch9003: 9003-Compat-Replace-BIO_MAX_VECS-by-BIO_MAX_PAGES.patch +Patch9004: 9004-Compat-Replace-readahead-by-readpages.patch +Patch9005: 9005-Compat-Add-fiemap_prep-function.patch +Patch9999: 9999-enable-ntfs3.patch ExclusiveArch: x86_64 aarch64 @@ -62,7 +173,7 @@ formatted with the Windows OS native file system (NTFS) up to version 3.1. %prep -%autosetup -p1 -n %{pkg}-%{version} +%autosetup -p1 -n %{pkg}-%{driver_version} %build @@ -124,14 +235,6 @@ fi %changelog -* Mon Nov 01 2021 Peter Georg - 5.15-4 -- Rebuild for 4.18.0-348.el8 - -* Mon Nov 01 2021 Peter Georg - 5.15-3 -- Rebuild for 4.18.0-338.el8 - -* Mon Nov 01 2021 Peter Georg - 5.15-2 -- Rebuild for 4.18.0-326.el8 - -* Mon Nov 01 2021 Peter Georg - 5.15-1 -- Initial version +* Thu Jan 13 2022 Kmods SIG - 4.18.0.0-1 +- Switch to EL kernel source and versioning +- kABI tracking kmod package (kernel >= 4.18.0-348.el8)