162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/blkdev.h> 962306a36Sopenharmony_ci#include <linux/buffer_head.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/nls.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "debug.h" 1562306a36Sopenharmony_ci#include "ntfs.h" 1662306a36Sopenharmony_ci#include "ntfs_fs.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci// clang-format off 1962306a36Sopenharmony_ciconst struct cpu_str NAME_MFT = { 2062306a36Sopenharmony_ci 4, 0, { '$', 'M', 'F', 'T' }, 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ciconst struct cpu_str NAME_MIRROR = { 2362306a36Sopenharmony_ci 8, 0, { '$', 'M', 'F', 'T', 'M', 'i', 'r', 'r' }, 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ciconst struct cpu_str NAME_LOGFILE = { 2662306a36Sopenharmony_ci 8, 0, { '$', 'L', 'o', 'g', 'F', 'i', 'l', 'e' }, 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ciconst struct cpu_str NAME_VOLUME = { 2962306a36Sopenharmony_ci 7, 0, { '$', 'V', 'o', 'l', 'u', 'm', 'e' }, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ciconst struct cpu_str NAME_ATTRDEF = { 3262306a36Sopenharmony_ci 8, 0, { '$', 'A', 't', 't', 'r', 'D', 'e', 'f' }, 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ciconst struct cpu_str NAME_ROOT = { 3562306a36Sopenharmony_ci 1, 0, { '.' }, 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ciconst struct cpu_str NAME_BITMAP = { 3862306a36Sopenharmony_ci 7, 0, { '$', 'B', 'i', 't', 'm', 'a', 'p' }, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ciconst struct cpu_str NAME_BOOT = { 4162306a36Sopenharmony_ci 5, 0, { '$', 'B', 'o', 'o', 't' }, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ciconst struct cpu_str NAME_BADCLUS = { 4462306a36Sopenharmony_ci 8, 0, { '$', 'B', 'a', 'd', 'C', 'l', 'u', 's' }, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ciconst struct cpu_str NAME_QUOTA = { 4762306a36Sopenharmony_ci 6, 0, { '$', 'Q', 'u', 'o', 't', 'a' }, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ciconst struct cpu_str NAME_SECURE = { 5062306a36Sopenharmony_ci 7, 0, { '$', 'S', 'e', 'c', 'u', 'r', 'e' }, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ciconst struct cpu_str NAME_UPCASE = { 5362306a36Sopenharmony_ci 7, 0, { '$', 'U', 'p', 'C', 'a', 's', 'e' }, 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ciconst struct cpu_str NAME_EXTEND = { 5662306a36Sopenharmony_ci 7, 0, { '$', 'E', 'x', 't', 'e', 'n', 'd' }, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ciconst struct cpu_str NAME_OBJID = { 5962306a36Sopenharmony_ci 6, 0, { '$', 'O', 'b', 'j', 'I', 'd' }, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ciconst struct cpu_str NAME_REPARSE = { 6262306a36Sopenharmony_ci 8, 0, { '$', 'R', 'e', 'p', 'a', 'r', 's', 'e' }, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ciconst struct cpu_str NAME_USNJRNL = { 6562306a36Sopenharmony_ci 8, 0, { '$', 'U', 's', 'n', 'J', 'r', 'n', 'l' }, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ciconst __le16 BAD_NAME[4] = { 6862306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('B'), cpu_to_le16('a'), cpu_to_le16('d'), 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ciconst __le16 I30_NAME[4] = { 7162306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('I'), cpu_to_le16('3'), cpu_to_le16('0'), 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ciconst __le16 SII_NAME[4] = { 7462306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('S'), cpu_to_le16('I'), cpu_to_le16('I'), 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ciconst __le16 SDH_NAME[4] = { 7762306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('S'), cpu_to_le16('D'), cpu_to_le16('H'), 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ciconst __le16 SDS_NAME[4] = { 8062306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('S'), cpu_to_le16('D'), cpu_to_le16('S'), 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ciconst __le16 SO_NAME[2] = { 8362306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('O'), 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ciconst __le16 SQ_NAME[2] = { 8662306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('Q'), 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ciconst __le16 SR_NAME[2] = { 8962306a36Sopenharmony_ci cpu_to_le16('$'), cpu_to_le16('R'), 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#ifdef CONFIG_NTFS3_LZX_XPRESS 9362306a36Sopenharmony_ciconst __le16 WOF_NAME[17] = { 9462306a36Sopenharmony_ci cpu_to_le16('W'), cpu_to_le16('o'), cpu_to_le16('f'), cpu_to_le16('C'), 9562306a36Sopenharmony_ci cpu_to_le16('o'), cpu_to_le16('m'), cpu_to_le16('p'), cpu_to_le16('r'), 9662306a36Sopenharmony_ci cpu_to_le16('e'), cpu_to_le16('s'), cpu_to_le16('s'), cpu_to_le16('e'), 9762306a36Sopenharmony_ci cpu_to_le16('d'), cpu_to_le16('D'), cpu_to_le16('a'), cpu_to_le16('t'), 9862306a36Sopenharmony_ci cpu_to_le16('a'), 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci#endif 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic const __le16 CON_NAME[3] = { 10362306a36Sopenharmony_ci cpu_to_le16('C'), cpu_to_le16('O'), cpu_to_le16('N'), 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const __le16 NUL_NAME[3] = { 10762306a36Sopenharmony_ci cpu_to_le16('N'), cpu_to_le16('U'), cpu_to_le16('L'), 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic const __le16 AUX_NAME[3] = { 11162306a36Sopenharmony_ci cpu_to_le16('A'), cpu_to_le16('U'), cpu_to_le16('X'), 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic const __le16 PRN_NAME[3] = { 11562306a36Sopenharmony_ci cpu_to_le16('P'), cpu_to_le16('R'), cpu_to_le16('N'), 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const __le16 COM_NAME[3] = { 11962306a36Sopenharmony_ci cpu_to_le16('C'), cpu_to_le16('O'), cpu_to_le16('M'), 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const __le16 LPT_NAME[3] = { 12362306a36Sopenharmony_ci cpu_to_le16('L'), cpu_to_le16('P'), cpu_to_le16('T'), 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci// clang-format on 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * ntfs_fix_pre_write - Insert fixups into @rhdr before writing to disk. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cibool ntfs_fix_pre_write(struct NTFS_RECORD_HEADER *rhdr, size_t bytes) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u16 *fixup, *ptr; 13462306a36Sopenharmony_ci u16 sample; 13562306a36Sopenharmony_ci u16 fo = le16_to_cpu(rhdr->fix_off); 13662306a36Sopenharmony_ci u16 fn = le16_to_cpu(rhdr->fix_num); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || 13962306a36Sopenharmony_ci fn * SECTOR_SIZE > bytes) { 14062306a36Sopenharmony_ci return false; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Get fixup pointer. */ 14462306a36Sopenharmony_ci fixup = Add2Ptr(rhdr, fo); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (*fixup >= 0x7FFF) 14762306a36Sopenharmony_ci *fixup = 1; 14862306a36Sopenharmony_ci else 14962306a36Sopenharmony_ci *fixup += 1; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci sample = *fixup; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ptr = Add2Ptr(rhdr, SECTOR_SIZE - sizeof(short)); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci while (fn--) { 15662306a36Sopenharmony_ci *++fixup = *ptr; 15762306a36Sopenharmony_ci *ptr = sample; 15862306a36Sopenharmony_ci ptr += SECTOR_SIZE / sizeof(short); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci return true; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * ntfs_fix_post_read - Remove fixups after reading from disk. 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Return: < 0 if error, 0 if ok, 1 if need to update fixups. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ciint ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes, 16962306a36Sopenharmony_ci bool simple) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci u16 *fixup, *ptr; 17362306a36Sopenharmony_ci u16 sample, fo, fn; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci fo = le16_to_cpu(rhdr->fix_off); 17662306a36Sopenharmony_ci fn = simple ? ((bytes >> SECTOR_SHIFT) + 1) : 17762306a36Sopenharmony_ci le16_to_cpu(rhdr->fix_num); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Check errors. */ 18062306a36Sopenharmony_ci if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || 18162306a36Sopenharmony_ci fn * SECTOR_SIZE > bytes) { 18262306a36Sopenharmony_ci return -E_NTFS_CORRUPT; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Get fixup pointer. */ 18662306a36Sopenharmony_ci fixup = Add2Ptr(rhdr, fo); 18762306a36Sopenharmony_ci sample = *fixup; 18862306a36Sopenharmony_ci ptr = Add2Ptr(rhdr, SECTOR_SIZE - sizeof(short)); 18962306a36Sopenharmony_ci ret = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci while (fn--) { 19262306a36Sopenharmony_ci /* Test current word. */ 19362306a36Sopenharmony_ci if (*ptr != sample) { 19462306a36Sopenharmony_ci /* Fixup does not match! Is it serious error? */ 19562306a36Sopenharmony_ci ret = -E_NTFS_FIXUP; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Replace fixup. */ 19962306a36Sopenharmony_ci *ptr = *++fixup; 20062306a36Sopenharmony_ci ptr += SECTOR_SIZE / sizeof(short); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * ntfs_extend_init - Load $Extend file. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ciint ntfs_extend_init(struct ntfs_sb_info *sbi) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci int err; 21262306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 21362306a36Sopenharmony_ci struct inode *inode, *inode2; 21462306a36Sopenharmony_ci struct MFT_REF ref; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (sbi->volume.major_ver < 3) { 21762306a36Sopenharmony_ci ntfs_notice(sb, "Skip $Extend 'cause NTFS version"); 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ref.low = cpu_to_le32(MFT_REC_EXTEND); 22262306a36Sopenharmony_ci ref.high = 0; 22362306a36Sopenharmony_ci ref.seq = cpu_to_le16(MFT_REC_EXTEND); 22462306a36Sopenharmony_ci inode = ntfs_iget5(sb, &ref, &NAME_EXTEND); 22562306a36Sopenharmony_ci if (IS_ERR(inode)) { 22662306a36Sopenharmony_ci err = PTR_ERR(inode); 22762306a36Sopenharmony_ci ntfs_err(sb, "Failed to load $Extend (%d).", err); 22862306a36Sopenharmony_ci inode = NULL; 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* If ntfs_iget5() reads from disk it never returns bad inode. */ 23362306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 23462306a36Sopenharmony_ci err = -EINVAL; 23562306a36Sopenharmony_ci goto out; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Try to find $ObjId */ 23962306a36Sopenharmony_ci inode2 = dir_search_u(inode, &NAME_OBJID, NULL); 24062306a36Sopenharmony_ci if (inode2 && !IS_ERR(inode2)) { 24162306a36Sopenharmony_ci if (is_bad_inode(inode2)) { 24262306a36Sopenharmony_ci iput(inode2); 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci sbi->objid.ni = ntfs_i(inode2); 24562306a36Sopenharmony_ci sbi->objid_no = inode2->i_ino; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Try to find $Quota */ 25062306a36Sopenharmony_ci inode2 = dir_search_u(inode, &NAME_QUOTA, NULL); 25162306a36Sopenharmony_ci if (inode2 && !IS_ERR(inode2)) { 25262306a36Sopenharmony_ci sbi->quota_no = inode2->i_ino; 25362306a36Sopenharmony_ci iput(inode2); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Try to find $Reparse */ 25762306a36Sopenharmony_ci inode2 = dir_search_u(inode, &NAME_REPARSE, NULL); 25862306a36Sopenharmony_ci if (inode2 && !IS_ERR(inode2)) { 25962306a36Sopenharmony_ci sbi->reparse.ni = ntfs_i(inode2); 26062306a36Sopenharmony_ci sbi->reparse_no = inode2->i_ino; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Try to find $UsnJrnl */ 26462306a36Sopenharmony_ci inode2 = dir_search_u(inode, &NAME_USNJRNL, NULL); 26562306a36Sopenharmony_ci if (inode2 && !IS_ERR(inode2)) { 26662306a36Sopenharmony_ci sbi->usn_jrnl_no = inode2->i_ino; 26762306a36Sopenharmony_ci iput(inode2); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci err = 0; 27162306a36Sopenharmony_ciout: 27262306a36Sopenharmony_ci iput(inode); 27362306a36Sopenharmony_ci return err; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ciint ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci int err = 0; 27962306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 28062306a36Sopenharmony_ci bool initialized = false; 28162306a36Sopenharmony_ci struct MFT_REF ref; 28262306a36Sopenharmony_ci struct inode *inode; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Check for 4GB. */ 28562306a36Sopenharmony_ci if (ni->vfs_inode.i_size >= 0x100000000ull) { 28662306a36Sopenharmony_ci ntfs_err(sb, "\x24LogFile is large than 4G."); 28762306a36Sopenharmony_ci err = -EINVAL; 28862306a36Sopenharmony_ci goto out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci sbi->flags |= NTFS_FLAGS_LOG_REPLAYING; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ref.low = cpu_to_le32(MFT_REC_MFT); 29462306a36Sopenharmony_ci ref.high = 0; 29562306a36Sopenharmony_ci ref.seq = cpu_to_le16(1); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci inode = ntfs_iget5(sb, &ref, NULL); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (IS_ERR(inode)) 30062306a36Sopenharmony_ci inode = NULL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!inode) { 30362306a36Sopenharmony_ci /* Try to use MFT copy. */ 30462306a36Sopenharmony_ci u64 t64 = sbi->mft.lbo; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci sbi->mft.lbo = sbi->mft.lbo2; 30762306a36Sopenharmony_ci inode = ntfs_iget5(sb, &ref, NULL); 30862306a36Sopenharmony_ci sbi->mft.lbo = t64; 30962306a36Sopenharmony_ci if (IS_ERR(inode)) 31062306a36Sopenharmony_ci inode = NULL; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!inode) { 31462306a36Sopenharmony_ci err = -EINVAL; 31562306a36Sopenharmony_ci ntfs_err(sb, "Failed to load $MFT."); 31662306a36Sopenharmony_ci goto out; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci sbi->mft.ni = ntfs_i(inode); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* LogFile should not contains attribute list. */ 32262306a36Sopenharmony_ci err = ni_load_all_mi(sbi->mft.ni); 32362306a36Sopenharmony_ci if (!err) 32462306a36Sopenharmony_ci err = log_replay(ni, &initialized); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci iput(inode); 32762306a36Sopenharmony_ci sbi->mft.ni = NULL; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci sync_blockdev(sb->s_bdev); 33062306a36Sopenharmony_ci invalidate_bdev(sb->s_bdev); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) { 33362306a36Sopenharmony_ci err = 0; 33462306a36Sopenharmony_ci goto out; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (sb_rdonly(sb) || !initialized) 33862306a36Sopenharmony_ci goto out; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Fill LogFile by '-1' if it is initialized. */ 34162306a36Sopenharmony_ci err = ntfs_bio_fill_1(sbi, &ni->file.run); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciout: 34462306a36Sopenharmony_ci sbi->flags &= ~NTFS_FLAGS_LOG_REPLAYING; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return err; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* 35062306a36Sopenharmony_ci * ntfs_look_for_free_space - Look for a free space in bitmap. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ciint ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len, 35362306a36Sopenharmony_ci CLST *new_lcn, CLST *new_len, 35462306a36Sopenharmony_ci enum ALLOCATE_OPT opt) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci int err; 35762306a36Sopenharmony_ci CLST alen; 35862306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 35962306a36Sopenharmony_ci size_t alcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen; 36062306a36Sopenharmony_ci struct wnd_bitmap *wnd = &sbi->used.bitmap; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); 36362306a36Sopenharmony_ci if (opt & ALLOCATE_MFT) { 36462306a36Sopenharmony_ci zlen = wnd_zone_len(wnd); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!zlen) { 36762306a36Sopenharmony_ci err = ntfs_refresh_zone(sbi); 36862306a36Sopenharmony_ci if (err) 36962306a36Sopenharmony_ci goto up_write; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci zlen = wnd_zone_len(wnd); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!zlen) { 37562306a36Sopenharmony_ci ntfs_err(sbi->sb, "no free space to extend mft"); 37662306a36Sopenharmony_ci err = -ENOSPC; 37762306a36Sopenharmony_ci goto up_write; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci lcn = wnd_zone_bit(wnd); 38162306a36Sopenharmony_ci alen = min_t(CLST, len, zlen); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci wnd_zone_set(wnd, lcn + alen, zlen - alen); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci err = wnd_set_used(wnd, lcn, alen); 38662306a36Sopenharmony_ci if (err) 38762306a36Sopenharmony_ci goto up_write; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci alcn = lcn; 39062306a36Sopenharmony_ci goto space_found; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * 'Cause cluster 0 is always used this value means that we should use 39462306a36Sopenharmony_ci * cached value of 'next_free_lcn' to improve performance. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci if (!lcn) 39762306a36Sopenharmony_ci lcn = sbi->used.next_free_lcn; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (lcn >= wnd->nbits) 40062306a36Sopenharmony_ci lcn = 0; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci alen = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &alcn); 40362306a36Sopenharmony_ci if (alen) 40462306a36Sopenharmony_ci goto space_found; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Try to use clusters from MftZone. */ 40762306a36Sopenharmony_ci zlen = wnd_zone_len(wnd); 40862306a36Sopenharmony_ci zeroes = wnd_zeroes(wnd); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Check too big request */ 41162306a36Sopenharmony_ci if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE) { 41262306a36Sopenharmony_ci err = -ENOSPC; 41362306a36Sopenharmony_ci goto up_write; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* How many clusters to cat from zone. */ 41762306a36Sopenharmony_ci zlcn = wnd_zone_bit(wnd); 41862306a36Sopenharmony_ci zlen2 = zlen >> 1; 41962306a36Sopenharmony_ci ztrim = clamp_val(len, zlen2, zlen); 42062306a36Sopenharmony_ci new_zlen = max_t(size_t, zlen - ztrim, NTFS_MIN_MFT_ZONE); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci wnd_zone_set(wnd, zlcn, new_zlen); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Allocate continues clusters. */ 42562306a36Sopenharmony_ci alen = wnd_find(wnd, len, 0, 42662306a36Sopenharmony_ci BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn); 42762306a36Sopenharmony_ci if (!alen) { 42862306a36Sopenharmony_ci err = -ENOSPC; 42962306a36Sopenharmony_ci goto up_write; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cispace_found: 43362306a36Sopenharmony_ci err = 0; 43462306a36Sopenharmony_ci *new_len = alen; 43562306a36Sopenharmony_ci *new_lcn = alcn; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ntfs_unmap_meta(sb, alcn, alen); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Set hint for next requests. */ 44062306a36Sopenharmony_ci if (!(opt & ALLOCATE_MFT)) 44162306a36Sopenharmony_ci sbi->used.next_free_lcn = alcn + alen; 44262306a36Sopenharmony_ciup_write: 44362306a36Sopenharmony_ci up_write(&wnd->rw_lock); 44462306a36Sopenharmony_ci return err; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci/* 44862306a36Sopenharmony_ci * ntfs_check_for_free_space 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * Check if it is possible to allocate 'clen' clusters and 'mlen' Mft records 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cibool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci size_t free, zlen, avail; 45562306a36Sopenharmony_ci struct wnd_bitmap *wnd; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci wnd = &sbi->used.bitmap; 45862306a36Sopenharmony_ci down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); 45962306a36Sopenharmony_ci free = wnd_zeroes(wnd); 46062306a36Sopenharmony_ci zlen = min_t(size_t, NTFS_MIN_MFT_ZONE, wnd_zone_len(wnd)); 46162306a36Sopenharmony_ci up_read(&wnd->rw_lock); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (free < zlen + clen) 46462306a36Sopenharmony_ci return false; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci avail = free - (zlen + clen); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci wnd = &sbi->mft.bitmap; 46962306a36Sopenharmony_ci down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT); 47062306a36Sopenharmony_ci free = wnd_zeroes(wnd); 47162306a36Sopenharmony_ci zlen = wnd_zone_len(wnd); 47262306a36Sopenharmony_ci up_read(&wnd->rw_lock); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (free >= zlen + mlen) 47562306a36Sopenharmony_ci return true; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return avail >= bytes_to_cluster(sbi, mlen << sbi->record_bits); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* 48162306a36Sopenharmony_ci * ntfs_extend_mft - Allocate additional MFT records. 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * sbi->mft.bitmap is locked for write. 48462306a36Sopenharmony_ci * 48562306a36Sopenharmony_ci * NOTE: recursive: 48662306a36Sopenharmony_ci * ntfs_look_free_mft -> 48762306a36Sopenharmony_ci * ntfs_extend_mft -> 48862306a36Sopenharmony_ci * attr_set_size -> 48962306a36Sopenharmony_ci * ni_insert_nonresident -> 49062306a36Sopenharmony_ci * ni_insert_attr -> 49162306a36Sopenharmony_ci * ni_ins_attr_ext -> 49262306a36Sopenharmony_ci * ntfs_look_free_mft -> 49362306a36Sopenharmony_ci * ntfs_extend_mft 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * To avoid recursive always allocate space for two new MFT records 49662306a36Sopenharmony_ci * see attrib.c: "at least two MFT to avoid recursive loop". 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_cistatic int ntfs_extend_mft(struct ntfs_sb_info *sbi) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci int err; 50162306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->mft.ni; 50262306a36Sopenharmony_ci size_t new_mft_total; 50362306a36Sopenharmony_ci u64 new_mft_bytes, new_bitmap_bytes; 50462306a36Sopenharmony_ci struct ATTRIB *attr; 50562306a36Sopenharmony_ci struct wnd_bitmap *wnd = &sbi->mft.bitmap; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci new_mft_total = ALIGN(wnd->nbits + NTFS_MFT_INCREASE_STEP, 128); 50862306a36Sopenharmony_ci new_mft_bytes = (u64)new_mft_total << sbi->record_bits; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Step 1: Resize $MFT::DATA. */ 51162306a36Sopenharmony_ci down_write(&ni->file.run_lock); 51262306a36Sopenharmony_ci err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, 51362306a36Sopenharmony_ci new_mft_bytes, NULL, false, &attr); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (err) { 51662306a36Sopenharmony_ci up_write(&ni->file.run_lock); 51762306a36Sopenharmony_ci goto out; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci attr->nres.valid_size = attr->nres.data_size; 52162306a36Sopenharmony_ci new_mft_total = le64_to_cpu(attr->nres.alloc_size) >> sbi->record_bits; 52262306a36Sopenharmony_ci ni->mi.dirty = true; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Step 2: Resize $MFT::BITMAP. */ 52562306a36Sopenharmony_ci new_bitmap_bytes = bitmap_size(new_mft_total); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci err = attr_set_size(ni, ATTR_BITMAP, NULL, 0, &sbi->mft.bitmap.run, 52862306a36Sopenharmony_ci new_bitmap_bytes, &new_bitmap_bytes, true, NULL); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Refresh MFT Zone if necessary. */ 53162306a36Sopenharmony_ci down_write_nested(&sbi->used.bitmap.rw_lock, BITMAP_MUTEX_CLUSTERS); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci ntfs_refresh_zone(sbi); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci up_write(&sbi->used.bitmap.rw_lock); 53662306a36Sopenharmony_ci up_write(&ni->file.run_lock); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (err) 53962306a36Sopenharmony_ci goto out; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci err = wnd_extend(wnd, new_mft_total); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (err) 54462306a36Sopenharmony_ci goto out; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci ntfs_clear_mft_tail(sbi, sbi->mft.used, new_mft_total); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci err = _ni_write_inode(&ni->vfs_inode, 0); 54962306a36Sopenharmony_ciout: 55062306a36Sopenharmony_ci return err; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/* 55462306a36Sopenharmony_ci * ntfs_look_free_mft - Look for a free MFT record. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ciint ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, 55762306a36Sopenharmony_ci struct ntfs_inode *ni, struct mft_inode **mi) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci int err = 0; 56062306a36Sopenharmony_ci size_t zbit, zlen, from, to, fr; 56162306a36Sopenharmony_ci size_t mft_total; 56262306a36Sopenharmony_ci struct MFT_REF ref; 56362306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 56462306a36Sopenharmony_ci struct wnd_bitmap *wnd = &sbi->mft.bitmap; 56562306a36Sopenharmony_ci u32 ir; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci static_assert(sizeof(sbi->mft.reserved_bitmap) * 8 >= 56862306a36Sopenharmony_ci MFT_REC_FREE - MFT_REC_RESERVED); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (!mft) 57162306a36Sopenharmony_ci down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci zlen = wnd_zone_len(wnd); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Always reserve space for MFT. */ 57662306a36Sopenharmony_ci if (zlen) { 57762306a36Sopenharmony_ci if (mft) { 57862306a36Sopenharmony_ci zbit = wnd_zone_bit(wnd); 57962306a36Sopenharmony_ci *rno = zbit; 58062306a36Sopenharmony_ci wnd_zone_set(wnd, zbit + 1, zlen - 1); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci goto found; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* No MFT zone. Find the nearest to '0' free MFT. */ 58662306a36Sopenharmony_ci if (!wnd_find(wnd, 1, MFT_REC_FREE, 0, &zbit)) { 58762306a36Sopenharmony_ci /* Resize MFT */ 58862306a36Sopenharmony_ci mft_total = wnd->nbits; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci err = ntfs_extend_mft(sbi); 59162306a36Sopenharmony_ci if (!err) { 59262306a36Sopenharmony_ci zbit = mft_total; 59362306a36Sopenharmony_ci goto reserve_mft; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (!mft || MFT_REC_FREE == sbi->mft.next_reserved) 59762306a36Sopenharmony_ci goto out; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci err = 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* 60262306a36Sopenharmony_ci * Look for free record reserved area [11-16) == 60362306a36Sopenharmony_ci * [MFT_REC_RESERVED, MFT_REC_FREE ) MFT bitmap always 60462306a36Sopenharmony_ci * marks it as used. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci if (!sbi->mft.reserved_bitmap) { 60762306a36Sopenharmony_ci /* Once per session create internal bitmap for 5 bits. */ 60862306a36Sopenharmony_ci sbi->mft.reserved_bitmap = 0xFF; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ref.high = 0; 61162306a36Sopenharmony_ci for (ir = MFT_REC_RESERVED; ir < MFT_REC_FREE; ir++) { 61262306a36Sopenharmony_ci struct inode *i; 61362306a36Sopenharmony_ci struct ntfs_inode *ni; 61462306a36Sopenharmony_ci struct MFT_REC *mrec; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci ref.low = cpu_to_le32(ir); 61762306a36Sopenharmony_ci ref.seq = cpu_to_le16(ir); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci i = ntfs_iget5(sb, &ref, NULL); 62062306a36Sopenharmony_ci if (IS_ERR(i)) { 62162306a36Sopenharmony_cinext: 62262306a36Sopenharmony_ci ntfs_notice( 62362306a36Sopenharmony_ci sb, 62462306a36Sopenharmony_ci "Invalid reserved record %x", 62562306a36Sopenharmony_ci ref.low); 62662306a36Sopenharmony_ci continue; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci if (is_bad_inode(i)) { 62962306a36Sopenharmony_ci iput(i); 63062306a36Sopenharmony_ci goto next; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ni = ntfs_i(i); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci mrec = ni->mi.mrec; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (!is_rec_base(mrec)) 63862306a36Sopenharmony_ci goto next; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (mrec->hard_links) 64162306a36Sopenharmony_ci goto next; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (!ni_std(ni)) 64462306a36Sopenharmony_ci goto next; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (ni_find_attr(ni, NULL, NULL, ATTR_NAME, 64762306a36Sopenharmony_ci NULL, 0, NULL, NULL)) 64862306a36Sopenharmony_ci goto next; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci __clear_bit(ir - MFT_REC_RESERVED, 65162306a36Sopenharmony_ci &sbi->mft.reserved_bitmap); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* Scan 5 bits for zero. Bit 0 == MFT_REC_RESERVED */ 65662306a36Sopenharmony_ci zbit = find_next_zero_bit(&sbi->mft.reserved_bitmap, 65762306a36Sopenharmony_ci MFT_REC_FREE, MFT_REC_RESERVED); 65862306a36Sopenharmony_ci if (zbit >= MFT_REC_FREE) { 65962306a36Sopenharmony_ci sbi->mft.next_reserved = MFT_REC_FREE; 66062306a36Sopenharmony_ci goto out; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci zlen = 1; 66462306a36Sopenharmony_ci sbi->mft.next_reserved = zbit; 66562306a36Sopenharmony_ci } else { 66662306a36Sopenharmony_cireserve_mft: 66762306a36Sopenharmony_ci zlen = zbit == MFT_REC_FREE ? (MFT_REC_USER - MFT_REC_FREE) : 4; 66862306a36Sopenharmony_ci if (zbit + zlen > wnd->nbits) 66962306a36Sopenharmony_ci zlen = wnd->nbits - zbit; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci while (zlen > 1 && !wnd_is_free(wnd, zbit, zlen)) 67262306a36Sopenharmony_ci zlen -= 1; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* [zbit, zbit + zlen) will be used for MFT itself. */ 67562306a36Sopenharmony_ci from = sbi->mft.used; 67662306a36Sopenharmony_ci if (from < zbit) 67762306a36Sopenharmony_ci from = zbit; 67862306a36Sopenharmony_ci to = zbit + zlen; 67962306a36Sopenharmony_ci if (from < to) { 68062306a36Sopenharmony_ci ntfs_clear_mft_tail(sbi, from, to); 68162306a36Sopenharmony_ci sbi->mft.used = to; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (mft) { 68662306a36Sopenharmony_ci *rno = zbit; 68762306a36Sopenharmony_ci zbit += 1; 68862306a36Sopenharmony_ci zlen -= 1; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci wnd_zone_set(wnd, zbit, zlen); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cifound: 69462306a36Sopenharmony_ci if (!mft) { 69562306a36Sopenharmony_ci /* The request to get record for general purpose. */ 69662306a36Sopenharmony_ci if (sbi->mft.next_free < MFT_REC_USER) 69762306a36Sopenharmony_ci sbi->mft.next_free = MFT_REC_USER; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci for (;;) { 70062306a36Sopenharmony_ci if (sbi->mft.next_free >= sbi->mft.bitmap.nbits) { 70162306a36Sopenharmony_ci } else if (!wnd_find(wnd, 1, MFT_REC_USER, 0, &fr)) { 70262306a36Sopenharmony_ci sbi->mft.next_free = sbi->mft.bitmap.nbits; 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci *rno = fr; 70562306a36Sopenharmony_ci sbi->mft.next_free = *rno + 1; 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci err = ntfs_extend_mft(sbi); 71062306a36Sopenharmony_ci if (err) 71162306a36Sopenharmony_ci goto out; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (ni && !ni_add_subrecord(ni, *rno, mi)) { 71662306a36Sopenharmony_ci err = -ENOMEM; 71762306a36Sopenharmony_ci goto out; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* We have found a record that are not reserved for next MFT. */ 72162306a36Sopenharmony_ci if (*rno >= MFT_REC_FREE) 72262306a36Sopenharmony_ci wnd_set_used(wnd, *rno, 1); 72362306a36Sopenharmony_ci else if (*rno >= MFT_REC_RESERVED && sbi->mft.reserved_bitmap_inited) 72462306a36Sopenharmony_ci __set_bit(*rno - MFT_REC_RESERVED, &sbi->mft.reserved_bitmap); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ciout: 72762306a36Sopenharmony_ci if (!mft) 72862306a36Sopenharmony_ci up_write(&wnd->rw_lock); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return err; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci/* 73462306a36Sopenharmony_ci * ntfs_mark_rec_free - Mark record as free. 73562306a36Sopenharmony_ci * is_mft - true if we are changing MFT 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_civoid ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct wnd_bitmap *wnd = &sbi->mft.bitmap; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (!is_mft) 74262306a36Sopenharmony_ci down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT); 74362306a36Sopenharmony_ci if (rno >= wnd->nbits) 74462306a36Sopenharmony_ci goto out; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (rno >= MFT_REC_FREE) { 74762306a36Sopenharmony_ci if (!wnd_is_used(wnd, rno, 1)) 74862306a36Sopenharmony_ci ntfs_set_state(sbi, NTFS_DIRTY_ERROR); 74962306a36Sopenharmony_ci else 75062306a36Sopenharmony_ci wnd_set_free(wnd, rno, 1); 75162306a36Sopenharmony_ci } else if (rno >= MFT_REC_RESERVED && sbi->mft.reserved_bitmap_inited) { 75262306a36Sopenharmony_ci __clear_bit(rno - MFT_REC_RESERVED, &sbi->mft.reserved_bitmap); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (rno < wnd_zone_bit(wnd)) 75662306a36Sopenharmony_ci wnd_zone_set(wnd, rno, 1); 75762306a36Sopenharmony_ci else if (rno < sbi->mft.next_free && rno >= MFT_REC_USER) 75862306a36Sopenharmony_ci sbi->mft.next_free = rno; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ciout: 76162306a36Sopenharmony_ci if (!is_mft) 76262306a36Sopenharmony_ci up_write(&wnd->rw_lock); 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci/* 76662306a36Sopenharmony_ci * ntfs_clear_mft_tail - Format empty records [from, to). 76762306a36Sopenharmony_ci * 76862306a36Sopenharmony_ci * sbi->mft.bitmap is locked for write. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ciint ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci int err; 77362306a36Sopenharmony_ci u32 rs; 77462306a36Sopenharmony_ci u64 vbo; 77562306a36Sopenharmony_ci struct runs_tree *run; 77662306a36Sopenharmony_ci struct ntfs_inode *ni; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (from >= to) 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci rs = sbi->record_size; 78262306a36Sopenharmony_ci ni = sbi->mft.ni; 78362306a36Sopenharmony_ci run = &ni->file.run; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci down_read(&ni->file.run_lock); 78662306a36Sopenharmony_ci vbo = (u64)from * rs; 78762306a36Sopenharmony_ci for (; from < to; from++, vbo += rs) { 78862306a36Sopenharmony_ci struct ntfs_buffers nb; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci err = ntfs_get_bh(sbi, run, vbo, rs, &nb); 79162306a36Sopenharmony_ci if (err) 79262306a36Sopenharmony_ci goto out; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci err = ntfs_write_bh(sbi, &sbi->new_rec->rhdr, &nb, 0); 79562306a36Sopenharmony_ci nb_put(&nb); 79662306a36Sopenharmony_ci if (err) 79762306a36Sopenharmony_ci goto out; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciout: 80162306a36Sopenharmony_ci sbi->mft.used = from; 80262306a36Sopenharmony_ci up_read(&ni->file.run_lock); 80362306a36Sopenharmony_ci return err; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* 80762306a36Sopenharmony_ci * ntfs_refresh_zone - Refresh MFT zone. 80862306a36Sopenharmony_ci * 80962306a36Sopenharmony_ci * sbi->used.bitmap is locked for rw. 81062306a36Sopenharmony_ci * sbi->mft.bitmap is locked for write. 81162306a36Sopenharmony_ci * sbi->mft.ni->file.run_lock for write. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ciint ntfs_refresh_zone(struct ntfs_sb_info *sbi) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci CLST lcn, vcn, len; 81662306a36Sopenharmony_ci size_t lcn_s, zlen; 81762306a36Sopenharmony_ci struct wnd_bitmap *wnd = &sbi->used.bitmap; 81862306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->mft.ni; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Do not change anything unless we have non empty MFT zone. */ 82162306a36Sopenharmony_ci if (wnd_zone_len(wnd)) 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci vcn = bytes_to_cluster(sbi, 82562306a36Sopenharmony_ci (u64)sbi->mft.bitmap.nbits << sbi->record_bits); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (!run_lookup_entry(&ni->file.run, vcn - 1, &lcn, &len, NULL)) 82862306a36Sopenharmony_ci lcn = SPARSE_LCN; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* We should always find Last Lcn for MFT. */ 83162306a36Sopenharmony_ci if (lcn == SPARSE_LCN) 83262306a36Sopenharmony_ci return -EINVAL; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci lcn_s = lcn + 1; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* Try to allocate clusters after last MFT run. */ 83762306a36Sopenharmony_ci zlen = wnd_find(wnd, sbi->zone_max, lcn_s, 0, &lcn_s); 83862306a36Sopenharmony_ci wnd_zone_set(wnd, lcn_s, zlen); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci/* 84462306a36Sopenharmony_ci * ntfs_update_mftmirr - Update $MFTMirr data. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_civoid ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci int err; 84962306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 85062306a36Sopenharmony_ci u32 blocksize, bytes; 85162306a36Sopenharmony_ci sector_t block1, block2; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* 85462306a36Sopenharmony_ci * sb can be NULL here. In this case sbi->flags should be 0 too. 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci if (!sb || !(sbi->flags & NTFS_FLAGS_MFTMIRR) || 85762306a36Sopenharmony_ci unlikely(ntfs3_forced_shutdown(sb))) 85862306a36Sopenharmony_ci return; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci blocksize = sb->s_blocksize; 86162306a36Sopenharmony_ci bytes = sbi->mft.recs_mirr << sbi->record_bits; 86262306a36Sopenharmony_ci block1 = sbi->mft.lbo >> sb->s_blocksize_bits; 86362306a36Sopenharmony_ci block2 = sbi->mft.lbo2 >> sb->s_blocksize_bits; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci for (; bytes >= blocksize; bytes -= blocksize) { 86662306a36Sopenharmony_ci struct buffer_head *bh1, *bh2; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci bh1 = sb_bread(sb, block1++); 86962306a36Sopenharmony_ci if (!bh1) 87062306a36Sopenharmony_ci return; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci bh2 = sb_getblk(sb, block2++); 87362306a36Sopenharmony_ci if (!bh2) { 87462306a36Sopenharmony_ci put_bh(bh1); 87562306a36Sopenharmony_ci return; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (buffer_locked(bh2)) 87962306a36Sopenharmony_ci __wait_on_buffer(bh2); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci lock_buffer(bh2); 88262306a36Sopenharmony_ci memcpy(bh2->b_data, bh1->b_data, blocksize); 88362306a36Sopenharmony_ci set_buffer_uptodate(bh2); 88462306a36Sopenharmony_ci mark_buffer_dirty(bh2); 88562306a36Sopenharmony_ci unlock_buffer(bh2); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci put_bh(bh1); 88862306a36Sopenharmony_ci bh1 = NULL; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci err = wait ? sync_dirty_buffer(bh2) : 0; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci put_bh(bh2); 89362306a36Sopenharmony_ci if (err) 89462306a36Sopenharmony_ci return; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci sbi->flags &= ~NTFS_FLAGS_MFTMIRR; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci/* 90162306a36Sopenharmony_ci * ntfs_bad_inode 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * Marks inode as bad and marks fs as 'dirty' 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_civoid ntfs_bad_inode(struct inode *inode, const char *hint) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci ntfs_inode_err(inode, "%s", hint); 91062306a36Sopenharmony_ci make_bad_inode(inode); 91162306a36Sopenharmony_ci ntfs_set_state(sbi, NTFS_DIRTY_ERROR); 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci/* 91562306a36Sopenharmony_ci * ntfs_set_state 91662306a36Sopenharmony_ci * 91762306a36Sopenharmony_ci * Mount: ntfs_set_state(NTFS_DIRTY_DIRTY) 91862306a36Sopenharmony_ci * Umount: ntfs_set_state(NTFS_DIRTY_CLEAR) 91962306a36Sopenharmony_ci * NTFS error: ntfs_set_state(NTFS_DIRTY_ERROR) 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ciint ntfs_set_state(struct ntfs_sb_info *sbi, enum NTFS_DIRTY_FLAGS dirty) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci int err; 92462306a36Sopenharmony_ci struct ATTRIB *attr; 92562306a36Sopenharmony_ci struct VOLUME_INFO *info; 92662306a36Sopenharmony_ci struct mft_inode *mi; 92762306a36Sopenharmony_ci struct ntfs_inode *ni; 92862306a36Sopenharmony_ci __le16 info_flags; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* 93162306a36Sopenharmony_ci * Do not change state if fs was real_dirty. 93262306a36Sopenharmony_ci * Do not change state if fs already dirty(clear). 93362306a36Sopenharmony_ci * Do not change any thing if mounted read only. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci if (sbi->volume.real_dirty || sb_rdonly(sbi->sb)) 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* Check cached value. */ 93962306a36Sopenharmony_ci if ((dirty == NTFS_DIRTY_CLEAR ? 0 : VOLUME_FLAG_DIRTY) == 94062306a36Sopenharmony_ci (sbi->volume.flags & VOLUME_FLAG_DIRTY)) 94162306a36Sopenharmony_ci return 0; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci ni = sbi->volume.ni; 94462306a36Sopenharmony_ci if (!ni) 94562306a36Sopenharmony_ci return -EINVAL; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_DIRTY); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci attr = ni_find_attr(ni, NULL, NULL, ATTR_VOL_INFO, NULL, 0, NULL, &mi); 95062306a36Sopenharmony_ci if (!attr) { 95162306a36Sopenharmony_ci err = -EINVAL; 95262306a36Sopenharmony_ci goto out; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci info = resident_data_ex(attr, SIZEOF_ATTRIBUTE_VOLUME_INFO); 95662306a36Sopenharmony_ci if (!info) { 95762306a36Sopenharmony_ci err = -EINVAL; 95862306a36Sopenharmony_ci goto out; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci info_flags = info->flags; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci switch (dirty) { 96462306a36Sopenharmony_ci case NTFS_DIRTY_ERROR: 96562306a36Sopenharmony_ci ntfs_notice(sbi->sb, "Mark volume as dirty due to NTFS errors"); 96662306a36Sopenharmony_ci sbi->volume.real_dirty = true; 96762306a36Sopenharmony_ci fallthrough; 96862306a36Sopenharmony_ci case NTFS_DIRTY_DIRTY: 96962306a36Sopenharmony_ci info->flags |= VOLUME_FLAG_DIRTY; 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci case NTFS_DIRTY_CLEAR: 97262306a36Sopenharmony_ci info->flags &= ~VOLUME_FLAG_DIRTY; 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci /* Cache current volume flags. */ 97662306a36Sopenharmony_ci if (info_flags != info->flags) { 97762306a36Sopenharmony_ci sbi->volume.flags = info->flags; 97862306a36Sopenharmony_ci mi->dirty = true; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci err = 0; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ciout: 98362306a36Sopenharmony_ci ni_unlock(ni); 98462306a36Sopenharmony_ci if (err) 98562306a36Sopenharmony_ci return err; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci mark_inode_dirty_sync(&ni->vfs_inode); 98862306a36Sopenharmony_ci /* verify(!ntfs_update_mftmirr()); */ 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* write mft record on disk. */ 99162306a36Sopenharmony_ci err = _ni_write_inode(&ni->vfs_inode, 1); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return err; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci/* 99762306a36Sopenharmony_ci * security_hash - Calculates a hash of security descriptor. 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_cistatic inline __le32 security_hash(const void *sd, size_t bytes) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci u32 hash = 0; 100262306a36Sopenharmony_ci const __le32 *ptr = sd; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci bytes >>= 2; 100562306a36Sopenharmony_ci while (bytes--) 100662306a36Sopenharmony_ci hash = ((hash >> 0x1D) | (hash << 3)) + le32_to_cpu(*ptr++); 100762306a36Sopenharmony_ci return cpu_to_le32(hash); 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci/* 101162306a36Sopenharmony_ci * simple wrapper for sb_bread_unmovable. 101262306a36Sopenharmony_ci */ 101362306a36Sopenharmony_cistruct buffer_head *ntfs_bread(struct super_block *sb, sector_t block) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct ntfs_sb_info *sbi = sb->s_fs_info; 101662306a36Sopenharmony_ci struct buffer_head *bh; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (unlikely(block >= sbi->volume.blocks)) { 101962306a36Sopenharmony_ci /* prevent generic message "attempt to access beyond end of device" */ 102062306a36Sopenharmony_ci ntfs_err(sb, "try to read out of volume at offset 0x%llx", 102162306a36Sopenharmony_ci (u64)block << sb->s_blocksize_bits); 102262306a36Sopenharmony_ci return NULL; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci bh = sb_bread_unmovable(sb, block); 102662306a36Sopenharmony_ci if (bh) 102762306a36Sopenharmony_ci return bh; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci ntfs_err(sb, "failed to read volume at offset 0x%llx", 103062306a36Sopenharmony_ci (u64)block << sb->s_blocksize_bits); 103162306a36Sopenharmony_ci return NULL; 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ciint ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct block_device *bdev = sb->s_bdev; 103762306a36Sopenharmony_ci u32 blocksize = sb->s_blocksize; 103862306a36Sopenharmony_ci u64 block = lbo >> sb->s_blocksize_bits; 103962306a36Sopenharmony_ci u32 off = lbo & (blocksize - 1); 104062306a36Sopenharmony_ci u32 op = blocksize - off; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci for (; bytes; block += 1, off = 0, op = blocksize) { 104362306a36Sopenharmony_ci struct buffer_head *bh = __bread(bdev, block, blocksize); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (!bh) 104662306a36Sopenharmony_ci return -EIO; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (op > bytes) 104962306a36Sopenharmony_ci op = bytes; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci memcpy(buffer, bh->b_data + off, op); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci put_bh(bh); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci bytes -= op; 105662306a36Sopenharmony_ci buffer = Add2Ptr(buffer, op); 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return 0; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ciint ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes, 106362306a36Sopenharmony_ci const void *buf, int wait) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci u32 blocksize = sb->s_blocksize; 106662306a36Sopenharmony_ci struct block_device *bdev = sb->s_bdev; 106762306a36Sopenharmony_ci sector_t block = lbo >> sb->s_blocksize_bits; 106862306a36Sopenharmony_ci u32 off = lbo & (blocksize - 1); 106962306a36Sopenharmony_ci u32 op = blocksize - off; 107062306a36Sopenharmony_ci struct buffer_head *bh; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (!wait && (sb->s_flags & SB_SYNCHRONOUS)) 107362306a36Sopenharmony_ci wait = 1; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci for (; bytes; block += 1, off = 0, op = blocksize) { 107662306a36Sopenharmony_ci if (op > bytes) 107762306a36Sopenharmony_ci op = bytes; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (op < blocksize) { 108062306a36Sopenharmony_ci bh = __bread(bdev, block, blocksize); 108162306a36Sopenharmony_ci if (!bh) { 108262306a36Sopenharmony_ci ntfs_err(sb, "failed to read block %llx", 108362306a36Sopenharmony_ci (u64)block); 108462306a36Sopenharmony_ci return -EIO; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci } else { 108762306a36Sopenharmony_ci bh = __getblk(bdev, block, blocksize); 108862306a36Sopenharmony_ci if (!bh) 108962306a36Sopenharmony_ci return -ENOMEM; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (buffer_locked(bh)) 109362306a36Sopenharmony_ci __wait_on_buffer(bh); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci lock_buffer(bh); 109662306a36Sopenharmony_ci if (buf) { 109762306a36Sopenharmony_ci memcpy(bh->b_data + off, buf, op); 109862306a36Sopenharmony_ci buf = Add2Ptr(buf, op); 109962306a36Sopenharmony_ci } else { 110062306a36Sopenharmony_ci memset(bh->b_data + off, -1, op); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci set_buffer_uptodate(bh); 110462306a36Sopenharmony_ci mark_buffer_dirty(bh); 110562306a36Sopenharmony_ci unlock_buffer(bh); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (wait) { 110862306a36Sopenharmony_ci int err = sync_dirty_buffer(bh); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (err) { 111162306a36Sopenharmony_ci ntfs_err( 111262306a36Sopenharmony_ci sb, 111362306a36Sopenharmony_ci "failed to sync buffer at block %llx, error %d", 111462306a36Sopenharmony_ci (u64)block, err); 111562306a36Sopenharmony_ci put_bh(bh); 111662306a36Sopenharmony_ci return err; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci put_bh(bh); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci bytes -= op; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ciint ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run, 112862306a36Sopenharmony_ci u64 vbo, const void *buf, size_t bytes, int sync) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 113162306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 113262306a36Sopenharmony_ci u32 off = vbo & sbi->cluster_mask; 113362306a36Sopenharmony_ci CLST lcn, clen, vcn = vbo >> cluster_bits, vcn_next; 113462306a36Sopenharmony_ci u64 lbo, len; 113562306a36Sopenharmony_ci size_t idx; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) 113862306a36Sopenharmony_ci return -ENOENT; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (lcn == SPARSE_LCN) 114162306a36Sopenharmony_ci return -EINVAL; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits) + off; 114462306a36Sopenharmony_ci len = ((u64)clen << cluster_bits) - off; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci for (;;) { 114762306a36Sopenharmony_ci u32 op = min_t(u64, len, bytes); 114862306a36Sopenharmony_ci int err = ntfs_sb_write(sb, lbo, op, buf, sync); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (err) 115162306a36Sopenharmony_ci return err; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci bytes -= op; 115462306a36Sopenharmony_ci if (!bytes) 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci vcn_next = vcn + clen; 115862306a36Sopenharmony_ci if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || 115962306a36Sopenharmony_ci vcn != vcn_next) 116062306a36Sopenharmony_ci return -ENOENT; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (lcn == SPARSE_LCN) 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (buf) 116662306a36Sopenharmony_ci buf = Add2Ptr(buf, op); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits); 116962306a36Sopenharmony_ci len = ((u64)clen << cluster_bits); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci return 0; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistruct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi, 117662306a36Sopenharmony_ci const struct runs_tree *run, u64 vbo) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 117962306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 118062306a36Sopenharmony_ci CLST lcn; 118162306a36Sopenharmony_ci u64 lbo; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (!run_lookup_entry(run, vbo >> cluster_bits, &lcn, NULL, NULL)) 118462306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits) + (vbo & sbi->cluster_mask); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return ntfs_bread(sb, lbo >> sb->s_blocksize_bits); 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ciint ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run, 119262306a36Sopenharmony_ci u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci int err; 119562306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 119662306a36Sopenharmony_ci u32 blocksize = sb->s_blocksize; 119762306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 119862306a36Sopenharmony_ci u32 off = vbo & sbi->cluster_mask; 119962306a36Sopenharmony_ci u32 nbh = 0; 120062306a36Sopenharmony_ci CLST vcn_next, vcn = vbo >> cluster_bits; 120162306a36Sopenharmony_ci CLST lcn, clen; 120262306a36Sopenharmony_ci u64 lbo, len; 120362306a36Sopenharmony_ci size_t idx; 120462306a36Sopenharmony_ci struct buffer_head *bh; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (!run) { 120762306a36Sopenharmony_ci /* First reading of $Volume + $MFTMirr + $LogFile goes here. */ 120862306a36Sopenharmony_ci if (vbo > MFT_REC_VOL * sbi->record_size) { 120962306a36Sopenharmony_ci err = -ENOENT; 121062306a36Sopenharmony_ci goto out; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* Use absolute boot's 'MFTCluster' to read record. */ 121462306a36Sopenharmony_ci lbo = vbo + sbi->mft.lbo; 121562306a36Sopenharmony_ci len = sbi->record_size; 121662306a36Sopenharmony_ci } else if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { 121762306a36Sopenharmony_ci err = -ENOENT; 121862306a36Sopenharmony_ci goto out; 121962306a36Sopenharmony_ci } else { 122062306a36Sopenharmony_ci if (lcn == SPARSE_LCN) { 122162306a36Sopenharmony_ci err = -EINVAL; 122262306a36Sopenharmony_ci goto out; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits) + off; 122662306a36Sopenharmony_ci len = ((u64)clen << cluster_bits) - off; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci off = lbo & (blocksize - 1); 123062306a36Sopenharmony_ci if (nb) { 123162306a36Sopenharmony_ci nb->off = off; 123262306a36Sopenharmony_ci nb->bytes = bytes; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci for (;;) { 123662306a36Sopenharmony_ci u32 len32 = len >= bytes ? bytes : len; 123762306a36Sopenharmony_ci sector_t block = lbo >> sb->s_blocksize_bits; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci do { 124062306a36Sopenharmony_ci u32 op = blocksize - off; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (op > len32) 124362306a36Sopenharmony_ci op = len32; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci bh = ntfs_bread(sb, block); 124662306a36Sopenharmony_ci if (!bh) { 124762306a36Sopenharmony_ci err = -EIO; 124862306a36Sopenharmony_ci goto out; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (buf) { 125262306a36Sopenharmony_ci memcpy(buf, bh->b_data + off, op); 125362306a36Sopenharmony_ci buf = Add2Ptr(buf, op); 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (!nb) { 125762306a36Sopenharmony_ci put_bh(bh); 125862306a36Sopenharmony_ci } else if (nbh >= ARRAY_SIZE(nb->bh)) { 125962306a36Sopenharmony_ci err = -EINVAL; 126062306a36Sopenharmony_ci goto out; 126162306a36Sopenharmony_ci } else { 126262306a36Sopenharmony_ci nb->bh[nbh++] = bh; 126362306a36Sopenharmony_ci nb->nbufs = nbh; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci bytes -= op; 126762306a36Sopenharmony_ci if (!bytes) 126862306a36Sopenharmony_ci return 0; 126962306a36Sopenharmony_ci len32 -= op; 127062306a36Sopenharmony_ci block += 1; 127162306a36Sopenharmony_ci off = 0; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci } while (len32); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci vcn_next = vcn + clen; 127662306a36Sopenharmony_ci if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || 127762306a36Sopenharmony_ci vcn != vcn_next) { 127862306a36Sopenharmony_ci err = -ENOENT; 127962306a36Sopenharmony_ci goto out; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci if (lcn == SPARSE_LCN) { 128362306a36Sopenharmony_ci err = -EINVAL; 128462306a36Sopenharmony_ci goto out; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits); 128862306a36Sopenharmony_ci len = ((u64)clen << cluster_bits); 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ciout: 129262306a36Sopenharmony_ci if (!nbh) 129362306a36Sopenharmony_ci return err; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci while (nbh) { 129662306a36Sopenharmony_ci put_bh(nb->bh[--nbh]); 129762306a36Sopenharmony_ci nb->bh[nbh] = NULL; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci nb->nbufs = 0; 130162306a36Sopenharmony_ci return err; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci/* 130562306a36Sopenharmony_ci * ntfs_read_bh 130662306a36Sopenharmony_ci * 130762306a36Sopenharmony_ci * Return: < 0 if error, 0 if ok, -E_NTFS_FIXUP if need to update fixups. 130862306a36Sopenharmony_ci */ 130962306a36Sopenharmony_ciint ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, 131062306a36Sopenharmony_ci struct NTFS_RECORD_HEADER *rhdr, u32 bytes, 131162306a36Sopenharmony_ci struct ntfs_buffers *nb) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci int err = ntfs_read_run_nb(sbi, run, vbo, rhdr, bytes, nb); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (err) 131662306a36Sopenharmony_ci return err; 131762306a36Sopenharmony_ci return ntfs_fix_post_read(rhdr, nb->bytes, true); 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ciint ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, 132162306a36Sopenharmony_ci u32 bytes, struct ntfs_buffers *nb) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci int err = 0; 132462306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 132562306a36Sopenharmony_ci u32 blocksize = sb->s_blocksize; 132662306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 132762306a36Sopenharmony_ci CLST vcn_next, vcn = vbo >> cluster_bits; 132862306a36Sopenharmony_ci u32 off; 132962306a36Sopenharmony_ci u32 nbh = 0; 133062306a36Sopenharmony_ci CLST lcn, clen; 133162306a36Sopenharmony_ci u64 lbo, len; 133262306a36Sopenharmony_ci size_t idx; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci nb->bytes = bytes; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (!run_lookup_entry(run, vcn, &lcn, &clen, &idx)) { 133762306a36Sopenharmony_ci err = -ENOENT; 133862306a36Sopenharmony_ci goto out; 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci off = vbo & sbi->cluster_mask; 134262306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits) + off; 134362306a36Sopenharmony_ci len = ((u64)clen << cluster_bits) - off; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci nb->off = off = lbo & (blocksize - 1); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci for (;;) { 134862306a36Sopenharmony_ci u32 len32 = min_t(u64, len, bytes); 134962306a36Sopenharmony_ci sector_t block = lbo >> sb->s_blocksize_bits; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci do { 135262306a36Sopenharmony_ci u32 op; 135362306a36Sopenharmony_ci struct buffer_head *bh; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (nbh >= ARRAY_SIZE(nb->bh)) { 135662306a36Sopenharmony_ci err = -EINVAL; 135762306a36Sopenharmony_ci goto out; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci op = blocksize - off; 136162306a36Sopenharmony_ci if (op > len32) 136262306a36Sopenharmony_ci op = len32; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (op == blocksize) { 136562306a36Sopenharmony_ci bh = sb_getblk(sb, block); 136662306a36Sopenharmony_ci if (!bh) { 136762306a36Sopenharmony_ci err = -ENOMEM; 136862306a36Sopenharmony_ci goto out; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci if (buffer_locked(bh)) 137162306a36Sopenharmony_ci __wait_on_buffer(bh); 137262306a36Sopenharmony_ci set_buffer_uptodate(bh); 137362306a36Sopenharmony_ci } else { 137462306a36Sopenharmony_ci bh = ntfs_bread(sb, block); 137562306a36Sopenharmony_ci if (!bh) { 137662306a36Sopenharmony_ci err = -EIO; 137762306a36Sopenharmony_ci goto out; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci nb->bh[nbh++] = bh; 138262306a36Sopenharmony_ci bytes -= op; 138362306a36Sopenharmony_ci if (!bytes) { 138462306a36Sopenharmony_ci nb->nbufs = nbh; 138562306a36Sopenharmony_ci return 0; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci block += 1; 138962306a36Sopenharmony_ci len32 -= op; 139062306a36Sopenharmony_ci off = 0; 139162306a36Sopenharmony_ci } while (len32); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci vcn_next = vcn + clen; 139462306a36Sopenharmony_ci if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) || 139562306a36Sopenharmony_ci vcn != vcn_next) { 139662306a36Sopenharmony_ci err = -ENOENT; 139762306a36Sopenharmony_ci goto out; 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits); 140162306a36Sopenharmony_ci len = ((u64)clen << cluster_bits); 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ciout: 140562306a36Sopenharmony_ci while (nbh) { 140662306a36Sopenharmony_ci put_bh(nb->bh[--nbh]); 140762306a36Sopenharmony_ci nb->bh[nbh] = NULL; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci nb->nbufs = 0; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci return err; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ciint ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr, 141662306a36Sopenharmony_ci struct ntfs_buffers *nb, int sync) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci int err = 0; 141962306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 142062306a36Sopenharmony_ci u32 block_size = sb->s_blocksize; 142162306a36Sopenharmony_ci u32 bytes = nb->bytes; 142262306a36Sopenharmony_ci u32 off = nb->off; 142362306a36Sopenharmony_ci u16 fo = le16_to_cpu(rhdr->fix_off); 142462306a36Sopenharmony_ci u16 fn = le16_to_cpu(rhdr->fix_num); 142562306a36Sopenharmony_ci u32 idx; 142662306a36Sopenharmony_ci __le16 *fixup; 142762306a36Sopenharmony_ci __le16 sample; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- || 143062306a36Sopenharmony_ci fn * SECTOR_SIZE > bytes) { 143162306a36Sopenharmony_ci return -EINVAL; 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci for (idx = 0; bytes && idx < nb->nbufs; idx += 1, off = 0) { 143562306a36Sopenharmony_ci u32 op = block_size - off; 143662306a36Sopenharmony_ci char *bh_data; 143762306a36Sopenharmony_ci struct buffer_head *bh = nb->bh[idx]; 143862306a36Sopenharmony_ci __le16 *ptr, *end_data; 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci if (op > bytes) 144162306a36Sopenharmony_ci op = bytes; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (buffer_locked(bh)) 144462306a36Sopenharmony_ci __wait_on_buffer(bh); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci lock_buffer(bh); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci bh_data = bh->b_data + off; 144962306a36Sopenharmony_ci end_data = Add2Ptr(bh_data, op); 145062306a36Sopenharmony_ci memcpy(bh_data, rhdr, op); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (!idx) { 145362306a36Sopenharmony_ci u16 t16; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci fixup = Add2Ptr(bh_data, fo); 145662306a36Sopenharmony_ci sample = *fixup; 145762306a36Sopenharmony_ci t16 = le16_to_cpu(sample); 145862306a36Sopenharmony_ci if (t16 >= 0x7FFF) { 145962306a36Sopenharmony_ci sample = *fixup = cpu_to_le16(1); 146062306a36Sopenharmony_ci } else { 146162306a36Sopenharmony_ci sample = cpu_to_le16(t16 + 1); 146262306a36Sopenharmony_ci *fixup = sample; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci *(__le16 *)Add2Ptr(rhdr, fo) = sample; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci ptr = Add2Ptr(bh_data, SECTOR_SIZE - sizeof(short)); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci do { 147162306a36Sopenharmony_ci *++fixup = *ptr; 147262306a36Sopenharmony_ci *ptr = sample; 147362306a36Sopenharmony_ci ptr += SECTOR_SIZE / sizeof(short); 147462306a36Sopenharmony_ci } while (ptr < end_data); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci set_buffer_uptodate(bh); 147762306a36Sopenharmony_ci mark_buffer_dirty(bh); 147862306a36Sopenharmony_ci unlock_buffer(bh); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (sync) { 148162306a36Sopenharmony_ci int err2 = sync_dirty_buffer(bh); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci if (!err && err2) 148462306a36Sopenharmony_ci err = err2; 148562306a36Sopenharmony_ci } 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci bytes -= op; 148862306a36Sopenharmony_ci rhdr = Add2Ptr(rhdr, op); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return err; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci/* 149562306a36Sopenharmony_ci * ntfs_bio_pages - Read/write pages from/to disk. 149662306a36Sopenharmony_ci */ 149762306a36Sopenharmony_ciint ntfs_bio_pages(struct ntfs_sb_info *sbi, const struct runs_tree *run, 149862306a36Sopenharmony_ci struct page **pages, u32 nr_pages, u64 vbo, u32 bytes, 149962306a36Sopenharmony_ci enum req_op op) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci int err = 0; 150262306a36Sopenharmony_ci struct bio *new, *bio = NULL; 150362306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 150462306a36Sopenharmony_ci struct block_device *bdev = sb->s_bdev; 150562306a36Sopenharmony_ci struct page *page; 150662306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 150762306a36Sopenharmony_ci CLST lcn, clen, vcn, vcn_next; 150862306a36Sopenharmony_ci u32 add, off, page_idx; 150962306a36Sopenharmony_ci u64 lbo, len; 151062306a36Sopenharmony_ci size_t run_idx; 151162306a36Sopenharmony_ci struct blk_plug plug; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci if (!bytes) 151462306a36Sopenharmony_ci return 0; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci blk_start_plug(&plug); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci /* Align vbo and bytes to be 512 bytes aligned. */ 151962306a36Sopenharmony_ci lbo = (vbo + bytes + 511) & ~511ull; 152062306a36Sopenharmony_ci vbo = vbo & ~511ull; 152162306a36Sopenharmony_ci bytes = lbo - vbo; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci vcn = vbo >> cluster_bits; 152462306a36Sopenharmony_ci if (!run_lookup_entry(run, vcn, &lcn, &clen, &run_idx)) { 152562306a36Sopenharmony_ci err = -ENOENT; 152662306a36Sopenharmony_ci goto out; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci off = vbo & sbi->cluster_mask; 152962306a36Sopenharmony_ci page_idx = 0; 153062306a36Sopenharmony_ci page = pages[0]; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci for (;;) { 153362306a36Sopenharmony_ci lbo = ((u64)lcn << cluster_bits) + off; 153462306a36Sopenharmony_ci len = ((u64)clen << cluster_bits) - off; 153562306a36Sopenharmony_cinew_bio: 153662306a36Sopenharmony_ci new = bio_alloc(bdev, nr_pages - page_idx, op, GFP_NOFS); 153762306a36Sopenharmony_ci if (bio) { 153862306a36Sopenharmony_ci bio_chain(bio, new); 153962306a36Sopenharmony_ci submit_bio(bio); 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci bio = new; 154262306a36Sopenharmony_ci bio->bi_iter.bi_sector = lbo >> 9; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci while (len) { 154562306a36Sopenharmony_ci off = vbo & (PAGE_SIZE - 1); 154662306a36Sopenharmony_ci add = off + len > PAGE_SIZE ? (PAGE_SIZE - off) : len; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (bio_add_page(bio, page, add, off) < add) 154962306a36Sopenharmony_ci goto new_bio; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci if (bytes <= add) 155262306a36Sopenharmony_ci goto out; 155362306a36Sopenharmony_ci bytes -= add; 155462306a36Sopenharmony_ci vbo += add; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (add + off == PAGE_SIZE) { 155762306a36Sopenharmony_ci page_idx += 1; 155862306a36Sopenharmony_ci if (WARN_ON(page_idx >= nr_pages)) { 155962306a36Sopenharmony_ci err = -EINVAL; 156062306a36Sopenharmony_ci goto out; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci page = pages[page_idx]; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (len <= add) 156662306a36Sopenharmony_ci break; 156762306a36Sopenharmony_ci len -= add; 156862306a36Sopenharmony_ci lbo += add; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci vcn_next = vcn + clen; 157262306a36Sopenharmony_ci if (!run_get_entry(run, ++run_idx, &vcn, &lcn, &clen) || 157362306a36Sopenharmony_ci vcn != vcn_next) { 157462306a36Sopenharmony_ci err = -ENOENT; 157562306a36Sopenharmony_ci goto out; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci off = 0; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ciout: 158062306a36Sopenharmony_ci if (bio) { 158162306a36Sopenharmony_ci if (!err) 158262306a36Sopenharmony_ci err = submit_bio_wait(bio); 158362306a36Sopenharmony_ci bio_put(bio); 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci blk_finish_plug(&plug); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci return err; 158862306a36Sopenharmony_ci} 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci/* 159162306a36Sopenharmony_ci * ntfs_bio_fill_1 - Helper for ntfs_loadlog_and_replay(). 159262306a36Sopenharmony_ci * 159362306a36Sopenharmony_ci * Fill on-disk logfile range by (-1) 159462306a36Sopenharmony_ci * this means empty logfile. 159562306a36Sopenharmony_ci */ 159662306a36Sopenharmony_ciint ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci int err = 0; 159962306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 160062306a36Sopenharmony_ci struct block_device *bdev = sb->s_bdev; 160162306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 160262306a36Sopenharmony_ci struct bio *new, *bio = NULL; 160362306a36Sopenharmony_ci CLST lcn, clen; 160462306a36Sopenharmony_ci u64 lbo, len; 160562306a36Sopenharmony_ci size_t run_idx; 160662306a36Sopenharmony_ci struct page *fill; 160762306a36Sopenharmony_ci void *kaddr; 160862306a36Sopenharmony_ci struct blk_plug plug; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci fill = alloc_page(GFP_KERNEL); 161162306a36Sopenharmony_ci if (!fill) 161262306a36Sopenharmony_ci return -ENOMEM; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci kaddr = kmap_atomic(fill); 161562306a36Sopenharmony_ci memset(kaddr, -1, PAGE_SIZE); 161662306a36Sopenharmony_ci kunmap_atomic(kaddr); 161762306a36Sopenharmony_ci flush_dcache_page(fill); 161862306a36Sopenharmony_ci lock_page(fill); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (!run_lookup_entry(run, 0, &lcn, &clen, &run_idx)) { 162162306a36Sopenharmony_ci err = -ENOENT; 162262306a36Sopenharmony_ci goto out; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci /* 162662306a36Sopenharmony_ci * TODO: Try blkdev_issue_write_same. 162762306a36Sopenharmony_ci */ 162862306a36Sopenharmony_ci blk_start_plug(&plug); 162962306a36Sopenharmony_ci do { 163062306a36Sopenharmony_ci lbo = (u64)lcn << cluster_bits; 163162306a36Sopenharmony_ci len = (u64)clen << cluster_bits; 163262306a36Sopenharmony_cinew_bio: 163362306a36Sopenharmony_ci new = bio_alloc(bdev, BIO_MAX_VECS, REQ_OP_WRITE, GFP_NOFS); 163462306a36Sopenharmony_ci if (bio) { 163562306a36Sopenharmony_ci bio_chain(bio, new); 163662306a36Sopenharmony_ci submit_bio(bio); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci bio = new; 163962306a36Sopenharmony_ci bio->bi_iter.bi_sector = lbo >> 9; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci for (;;) { 164262306a36Sopenharmony_ci u32 add = len > PAGE_SIZE ? PAGE_SIZE : len; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (bio_add_page(bio, fill, add, 0) < add) 164562306a36Sopenharmony_ci goto new_bio; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci lbo += add; 164862306a36Sopenharmony_ci if (len <= add) 164962306a36Sopenharmony_ci break; 165062306a36Sopenharmony_ci len -= add; 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci } while (run_get_entry(run, ++run_idx, NULL, &lcn, &clen)); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci if (!err) 165562306a36Sopenharmony_ci err = submit_bio_wait(bio); 165662306a36Sopenharmony_ci bio_put(bio); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci blk_finish_plug(&plug); 165962306a36Sopenharmony_ciout: 166062306a36Sopenharmony_ci unlock_page(fill); 166162306a36Sopenharmony_ci put_page(fill); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci return err; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ciint ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run, 166762306a36Sopenharmony_ci u64 vbo, u64 *lbo, u64 *bytes) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci u32 off; 167062306a36Sopenharmony_ci CLST lcn, len; 167162306a36Sopenharmony_ci u8 cluster_bits = sbi->cluster_bits; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci if (!run_lookup_entry(run, vbo >> cluster_bits, &lcn, &len, NULL)) 167462306a36Sopenharmony_ci return -ENOENT; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci off = vbo & sbi->cluster_mask; 167762306a36Sopenharmony_ci *lbo = lcn == SPARSE_LCN ? -1 : (((u64)lcn << cluster_bits) + off); 167862306a36Sopenharmony_ci *bytes = ((u64)len << cluster_bits) - off; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci return 0; 168162306a36Sopenharmony_ci} 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistruct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, 168462306a36Sopenharmony_ci enum RECORD_FLAG flag) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci int err = 0; 168762306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 168862306a36Sopenharmony_ci struct inode *inode = new_inode(sb); 168962306a36Sopenharmony_ci struct ntfs_inode *ni; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci if (!inode) 169262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci ni = ntfs_i(inode); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci err = mi_format_new(&ni->mi, sbi, rno, flag, false); 169762306a36Sopenharmony_ci if (err) 169862306a36Sopenharmony_ci goto out; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci inode->i_ino = rno; 170162306a36Sopenharmony_ci if (insert_inode_locked(inode) < 0) { 170262306a36Sopenharmony_ci err = -EIO; 170362306a36Sopenharmony_ci goto out; 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ciout: 170762306a36Sopenharmony_ci if (err) { 170862306a36Sopenharmony_ci make_bad_inode(inode); 170962306a36Sopenharmony_ci iput(inode); 171062306a36Sopenharmony_ci ni = ERR_PTR(err); 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci return ni; 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci/* 171662306a36Sopenharmony_ci * O:BAG:BAD:(A;OICI;FA;;;WD) 171762306a36Sopenharmony_ci * Owner S-1-5-32-544 (Administrators) 171862306a36Sopenharmony_ci * Group S-1-5-32-544 (Administrators) 171962306a36Sopenharmony_ci * ACE: allow S-1-1-0 (Everyone) with FILE_ALL_ACCESS 172062306a36Sopenharmony_ci */ 172162306a36Sopenharmony_ciconst u8 s_default_security[] __aligned(8) = { 172262306a36Sopenharmony_ci 0x01, 0x00, 0x04, 0x80, 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 172362306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1C, 0x00, 172462306a36Sopenharmony_ci 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x14, 0x00, 0xFF, 0x01, 0x1F, 0x00, 172562306a36Sopenharmony_ci 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 172662306a36Sopenharmony_ci 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 172762306a36Sopenharmony_ci 0x20, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 172862306a36Sopenharmony_ci 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 172962306a36Sopenharmony_ci}; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cistatic_assert(sizeof(s_default_security) == 0x50); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cistatic inline u32 sid_length(const struct SID *sid) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci return struct_size(sid, SubAuthority, sid->SubAuthorityCount); 173662306a36Sopenharmony_ci} 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci/* 173962306a36Sopenharmony_ci * is_acl_valid 174062306a36Sopenharmony_ci * 174162306a36Sopenharmony_ci * Thanks Mark Harmstone for idea. 174262306a36Sopenharmony_ci */ 174362306a36Sopenharmony_cistatic bool is_acl_valid(const struct ACL *acl, u32 len) 174462306a36Sopenharmony_ci{ 174562306a36Sopenharmony_ci const struct ACE_HEADER *ace; 174662306a36Sopenharmony_ci u32 i; 174762306a36Sopenharmony_ci u16 ace_count, ace_size; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci if (acl->AclRevision != ACL_REVISION && 175062306a36Sopenharmony_ci acl->AclRevision != ACL_REVISION_DS) { 175162306a36Sopenharmony_ci /* 175262306a36Sopenharmony_ci * This value should be ACL_REVISION, unless the ACL contains an 175362306a36Sopenharmony_ci * object-specific ACE, in which case this value must be ACL_REVISION_DS. 175462306a36Sopenharmony_ci * All ACEs in an ACL must be at the same revision level. 175562306a36Sopenharmony_ci */ 175662306a36Sopenharmony_ci return false; 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci if (acl->Sbz1) 176062306a36Sopenharmony_ci return false; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (le16_to_cpu(acl->AclSize) > len) 176362306a36Sopenharmony_ci return false; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci if (acl->Sbz2) 176662306a36Sopenharmony_ci return false; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci len -= sizeof(struct ACL); 176962306a36Sopenharmony_ci ace = (struct ACE_HEADER *)&acl[1]; 177062306a36Sopenharmony_ci ace_count = le16_to_cpu(acl->AceCount); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci for (i = 0; i < ace_count; i++) { 177362306a36Sopenharmony_ci if (len < sizeof(struct ACE_HEADER)) 177462306a36Sopenharmony_ci return false; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci ace_size = le16_to_cpu(ace->AceSize); 177762306a36Sopenharmony_ci if (len < ace_size) 177862306a36Sopenharmony_ci return false; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci len -= ace_size; 178162306a36Sopenharmony_ci ace = Add2Ptr(ace, ace_size); 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci return true; 178562306a36Sopenharmony_ci} 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_cibool is_sd_valid(const struct SECURITY_DESCRIPTOR_RELATIVE *sd, u32 len) 178862306a36Sopenharmony_ci{ 178962306a36Sopenharmony_ci u32 sd_owner, sd_group, sd_sacl, sd_dacl; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci if (len < sizeof(struct SECURITY_DESCRIPTOR_RELATIVE)) 179262306a36Sopenharmony_ci return false; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci if (sd->Revision != 1) 179562306a36Sopenharmony_ci return false; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (sd->Sbz1) 179862306a36Sopenharmony_ci return false; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci if (!(sd->Control & SE_SELF_RELATIVE)) 180162306a36Sopenharmony_ci return false; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci sd_owner = le32_to_cpu(sd->Owner); 180462306a36Sopenharmony_ci if (sd_owner) { 180562306a36Sopenharmony_ci const struct SID *owner = Add2Ptr(sd, sd_owner); 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (sd_owner + offsetof(struct SID, SubAuthority) > len) 180862306a36Sopenharmony_ci return false; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if (owner->Revision != 1) 181162306a36Sopenharmony_ci return false; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci if (sd_owner + sid_length(owner) > len) 181462306a36Sopenharmony_ci return false; 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci sd_group = le32_to_cpu(sd->Group); 181862306a36Sopenharmony_ci if (sd_group) { 181962306a36Sopenharmony_ci const struct SID *group = Add2Ptr(sd, sd_group); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci if (sd_group + offsetof(struct SID, SubAuthority) > len) 182262306a36Sopenharmony_ci return false; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci if (group->Revision != 1) 182562306a36Sopenharmony_ci return false; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci if (sd_group + sid_length(group) > len) 182862306a36Sopenharmony_ci return false; 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci sd_sacl = le32_to_cpu(sd->Sacl); 183262306a36Sopenharmony_ci if (sd_sacl) { 183362306a36Sopenharmony_ci const struct ACL *sacl = Add2Ptr(sd, sd_sacl); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci if (sd_sacl + sizeof(struct ACL) > len) 183662306a36Sopenharmony_ci return false; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci if (!is_acl_valid(sacl, len - sd_sacl)) 183962306a36Sopenharmony_ci return false; 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci sd_dacl = le32_to_cpu(sd->Dacl); 184362306a36Sopenharmony_ci if (sd_dacl) { 184462306a36Sopenharmony_ci const struct ACL *dacl = Add2Ptr(sd, sd_dacl); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci if (sd_dacl + sizeof(struct ACL) > len) 184762306a36Sopenharmony_ci return false; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci if (!is_acl_valid(dacl, len - sd_dacl)) 185062306a36Sopenharmony_ci return false; 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci return true; 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci/* 185762306a36Sopenharmony_ci * ntfs_security_init - Load and parse $Secure. 185862306a36Sopenharmony_ci */ 185962306a36Sopenharmony_ciint ntfs_security_init(struct ntfs_sb_info *sbi) 186062306a36Sopenharmony_ci{ 186162306a36Sopenharmony_ci int err; 186262306a36Sopenharmony_ci struct super_block *sb = sbi->sb; 186362306a36Sopenharmony_ci struct inode *inode; 186462306a36Sopenharmony_ci struct ntfs_inode *ni; 186562306a36Sopenharmony_ci struct MFT_REF ref; 186662306a36Sopenharmony_ci struct ATTRIB *attr; 186762306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le; 186862306a36Sopenharmony_ci u64 sds_size; 186962306a36Sopenharmony_ci size_t off; 187062306a36Sopenharmony_ci struct NTFS_DE *ne; 187162306a36Sopenharmony_ci struct NTFS_DE_SII *sii_e; 187262306a36Sopenharmony_ci struct ntfs_fnd *fnd_sii = NULL; 187362306a36Sopenharmony_ci const struct INDEX_ROOT *root_sii; 187462306a36Sopenharmony_ci const struct INDEX_ROOT *root_sdh; 187562306a36Sopenharmony_ci struct ntfs_index *indx_sdh = &sbi->security.index_sdh; 187662306a36Sopenharmony_ci struct ntfs_index *indx_sii = &sbi->security.index_sii; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci ref.low = cpu_to_le32(MFT_REC_SECURE); 187962306a36Sopenharmony_ci ref.high = 0; 188062306a36Sopenharmony_ci ref.seq = cpu_to_le16(MFT_REC_SECURE); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci inode = ntfs_iget5(sb, &ref, &NAME_SECURE); 188362306a36Sopenharmony_ci if (IS_ERR(inode)) { 188462306a36Sopenharmony_ci err = PTR_ERR(inode); 188562306a36Sopenharmony_ci ntfs_err(sb, "Failed to load $Secure (%d).", err); 188662306a36Sopenharmony_ci inode = NULL; 188762306a36Sopenharmony_ci goto out; 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci ni = ntfs_i(inode); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci le = NULL; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci attr = ni_find_attr(ni, NULL, &le, ATTR_ROOT, SDH_NAME, 189562306a36Sopenharmony_ci ARRAY_SIZE(SDH_NAME), NULL, NULL); 189662306a36Sopenharmony_ci if (!attr || 189762306a36Sopenharmony_ci !(root_sdh = resident_data_ex(attr, sizeof(struct INDEX_ROOT))) || 189862306a36Sopenharmony_ci root_sdh->type != ATTR_ZERO || 189962306a36Sopenharmony_ci root_sdh->rule != NTFS_COLLATION_TYPE_SECURITY_HASH || 190062306a36Sopenharmony_ci offsetof(struct INDEX_ROOT, ihdr) + 190162306a36Sopenharmony_ci le32_to_cpu(root_sdh->ihdr.used) > 190262306a36Sopenharmony_ci le32_to_cpu(attr->res.data_size)) { 190362306a36Sopenharmony_ci ntfs_err(sb, "$Secure::$SDH is corrupted."); 190462306a36Sopenharmony_ci err = -EINVAL; 190562306a36Sopenharmony_ci goto out; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci err = indx_init(indx_sdh, sbi, attr, INDEX_MUTEX_SDH); 190962306a36Sopenharmony_ci if (err) { 191062306a36Sopenharmony_ci ntfs_err(sb, "Failed to initialize $Secure::$SDH (%d).", err); 191162306a36Sopenharmony_ci goto out; 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci attr = ni_find_attr(ni, attr, &le, ATTR_ROOT, SII_NAME, 191562306a36Sopenharmony_ci ARRAY_SIZE(SII_NAME), NULL, NULL); 191662306a36Sopenharmony_ci if (!attr || 191762306a36Sopenharmony_ci !(root_sii = resident_data_ex(attr, sizeof(struct INDEX_ROOT))) || 191862306a36Sopenharmony_ci root_sii->type != ATTR_ZERO || 191962306a36Sopenharmony_ci root_sii->rule != NTFS_COLLATION_TYPE_UINT || 192062306a36Sopenharmony_ci offsetof(struct INDEX_ROOT, ihdr) + 192162306a36Sopenharmony_ci le32_to_cpu(root_sii->ihdr.used) > 192262306a36Sopenharmony_ci le32_to_cpu(attr->res.data_size)) { 192362306a36Sopenharmony_ci ntfs_err(sb, "$Secure::$SII is corrupted."); 192462306a36Sopenharmony_ci err = -EINVAL; 192562306a36Sopenharmony_ci goto out; 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci err = indx_init(indx_sii, sbi, attr, INDEX_MUTEX_SII); 192962306a36Sopenharmony_ci if (err) { 193062306a36Sopenharmony_ci ntfs_err(sb, "Failed to initialize $Secure::$SII (%d).", err); 193162306a36Sopenharmony_ci goto out; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci fnd_sii = fnd_get(); 193562306a36Sopenharmony_ci if (!fnd_sii) { 193662306a36Sopenharmony_ci err = -ENOMEM; 193762306a36Sopenharmony_ci goto out; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci sds_size = inode->i_size; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci /* Find the last valid Id. */ 194362306a36Sopenharmony_ci sbi->security.next_id = SECURITY_ID_FIRST; 194462306a36Sopenharmony_ci /* Always write new security at the end of bucket. */ 194562306a36Sopenharmony_ci sbi->security.next_off = 194662306a36Sopenharmony_ci ALIGN(sds_size - SecurityDescriptorsBlockSize, 16); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci off = 0; 194962306a36Sopenharmony_ci ne = NULL; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci for (;;) { 195262306a36Sopenharmony_ci u32 next_id; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci err = indx_find_raw(indx_sii, ni, root_sii, &ne, &off, fnd_sii); 195562306a36Sopenharmony_ci if (err || !ne) 195662306a36Sopenharmony_ci break; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci sii_e = (struct NTFS_DE_SII *)ne; 195962306a36Sopenharmony_ci if (le16_to_cpu(ne->view.data_size) < sizeof(sii_e->sec_hdr)) 196062306a36Sopenharmony_ci continue; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci next_id = le32_to_cpu(sii_e->sec_id) + 1; 196362306a36Sopenharmony_ci if (next_id >= sbi->security.next_id) 196462306a36Sopenharmony_ci sbi->security.next_id = next_id; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci sbi->security.ni = ni; 196862306a36Sopenharmony_ci inode = NULL; 196962306a36Sopenharmony_ciout: 197062306a36Sopenharmony_ci iput(inode); 197162306a36Sopenharmony_ci fnd_put(fnd_sii); 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci return err; 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci/* 197762306a36Sopenharmony_ci * ntfs_get_security_by_id - Read security descriptor by id. 197862306a36Sopenharmony_ci */ 197962306a36Sopenharmony_ciint ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id, 198062306a36Sopenharmony_ci struct SECURITY_DESCRIPTOR_RELATIVE **sd, 198162306a36Sopenharmony_ci size_t *size) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci int err; 198462306a36Sopenharmony_ci int diff; 198562306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->security.ni; 198662306a36Sopenharmony_ci struct ntfs_index *indx = &sbi->security.index_sii; 198762306a36Sopenharmony_ci void *p = NULL; 198862306a36Sopenharmony_ci struct NTFS_DE_SII *sii_e; 198962306a36Sopenharmony_ci struct ntfs_fnd *fnd_sii; 199062306a36Sopenharmony_ci struct SECURITY_HDR d_security; 199162306a36Sopenharmony_ci const struct INDEX_ROOT *root_sii; 199262306a36Sopenharmony_ci u32 t32; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci *sd = NULL; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_SECURITY); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci fnd_sii = fnd_get(); 199962306a36Sopenharmony_ci if (!fnd_sii) { 200062306a36Sopenharmony_ci err = -ENOMEM; 200162306a36Sopenharmony_ci goto out; 200262306a36Sopenharmony_ci } 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci root_sii = indx_get_root(indx, ni, NULL, NULL); 200562306a36Sopenharmony_ci if (!root_sii) { 200662306a36Sopenharmony_ci err = -EINVAL; 200762306a36Sopenharmony_ci goto out; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci /* Try to find this SECURITY descriptor in SII indexes. */ 201162306a36Sopenharmony_ci err = indx_find(indx, ni, root_sii, &security_id, sizeof(security_id), 201262306a36Sopenharmony_ci NULL, &diff, (struct NTFS_DE **)&sii_e, fnd_sii); 201362306a36Sopenharmony_ci if (err) 201462306a36Sopenharmony_ci goto out; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (diff) 201762306a36Sopenharmony_ci goto out; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci t32 = le32_to_cpu(sii_e->sec_hdr.size); 202062306a36Sopenharmony_ci if (t32 < sizeof(struct SECURITY_HDR)) { 202162306a36Sopenharmony_ci err = -EINVAL; 202262306a36Sopenharmony_ci goto out; 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci if (t32 > sizeof(struct SECURITY_HDR) + 0x10000) { 202662306a36Sopenharmony_ci /* Looks like too big security. 0x10000 - is arbitrary big number. */ 202762306a36Sopenharmony_ci err = -EFBIG; 202862306a36Sopenharmony_ci goto out; 202962306a36Sopenharmony_ci } 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci *size = t32 - sizeof(struct SECURITY_HDR); 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci p = kmalloc(*size, GFP_NOFS); 203462306a36Sopenharmony_ci if (!p) { 203562306a36Sopenharmony_ci err = -ENOMEM; 203662306a36Sopenharmony_ci goto out; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci err = ntfs_read_run_nb(sbi, &ni->file.run, 204062306a36Sopenharmony_ci le64_to_cpu(sii_e->sec_hdr.off), &d_security, 204162306a36Sopenharmony_ci sizeof(d_security), NULL); 204262306a36Sopenharmony_ci if (err) 204362306a36Sopenharmony_ci goto out; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (memcmp(&d_security, &sii_e->sec_hdr, sizeof(d_security))) { 204662306a36Sopenharmony_ci err = -EINVAL; 204762306a36Sopenharmony_ci goto out; 204862306a36Sopenharmony_ci } 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci err = ntfs_read_run_nb(sbi, &ni->file.run, 205162306a36Sopenharmony_ci le64_to_cpu(sii_e->sec_hdr.off) + 205262306a36Sopenharmony_ci sizeof(struct SECURITY_HDR), 205362306a36Sopenharmony_ci p, *size, NULL); 205462306a36Sopenharmony_ci if (err) 205562306a36Sopenharmony_ci goto out; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci *sd = p; 205862306a36Sopenharmony_ci p = NULL; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ciout: 206162306a36Sopenharmony_ci kfree(p); 206262306a36Sopenharmony_ci fnd_put(fnd_sii); 206362306a36Sopenharmony_ci ni_unlock(ni); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci return err; 206662306a36Sopenharmony_ci} 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci/* 206962306a36Sopenharmony_ci * ntfs_insert_security - Insert security descriptor into $Secure::SDS. 207062306a36Sopenharmony_ci * 207162306a36Sopenharmony_ci * SECURITY Descriptor Stream data is organized into chunks of 256K bytes 207262306a36Sopenharmony_ci * and it contains a mirror copy of each security descriptor. When writing 207362306a36Sopenharmony_ci * to a security descriptor at location X, another copy will be written at 207462306a36Sopenharmony_ci * location (X+256K). 207562306a36Sopenharmony_ci * When writing a security descriptor that will cross the 256K boundary, 207662306a36Sopenharmony_ci * the pointer will be advanced by 256K to skip 207762306a36Sopenharmony_ci * over the mirror portion. 207862306a36Sopenharmony_ci */ 207962306a36Sopenharmony_ciint ntfs_insert_security(struct ntfs_sb_info *sbi, 208062306a36Sopenharmony_ci const struct SECURITY_DESCRIPTOR_RELATIVE *sd, 208162306a36Sopenharmony_ci u32 size_sd, __le32 *security_id, bool *inserted) 208262306a36Sopenharmony_ci{ 208362306a36Sopenharmony_ci int err, diff; 208462306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->security.ni; 208562306a36Sopenharmony_ci struct ntfs_index *indx_sdh = &sbi->security.index_sdh; 208662306a36Sopenharmony_ci struct ntfs_index *indx_sii = &sbi->security.index_sii; 208762306a36Sopenharmony_ci struct NTFS_DE_SDH *e; 208862306a36Sopenharmony_ci struct NTFS_DE_SDH sdh_e; 208962306a36Sopenharmony_ci struct NTFS_DE_SII sii_e; 209062306a36Sopenharmony_ci struct SECURITY_HDR *d_security; 209162306a36Sopenharmony_ci u32 new_sec_size = size_sd + sizeof(struct SECURITY_HDR); 209262306a36Sopenharmony_ci u32 aligned_sec_size = ALIGN(new_sec_size, 16); 209362306a36Sopenharmony_ci struct SECURITY_KEY hash_key; 209462306a36Sopenharmony_ci struct ntfs_fnd *fnd_sdh = NULL; 209562306a36Sopenharmony_ci const struct INDEX_ROOT *root_sdh; 209662306a36Sopenharmony_ci const struct INDEX_ROOT *root_sii; 209762306a36Sopenharmony_ci u64 mirr_off, new_sds_size; 209862306a36Sopenharmony_ci u32 next, left; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci static_assert((1 << Log2OfSecurityDescriptorsBlockSize) == 210162306a36Sopenharmony_ci SecurityDescriptorsBlockSize); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci hash_key.hash = security_hash(sd, size_sd); 210462306a36Sopenharmony_ci hash_key.sec_id = SECURITY_ID_INVALID; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci if (inserted) 210762306a36Sopenharmony_ci *inserted = false; 210862306a36Sopenharmony_ci *security_id = SECURITY_ID_INVALID; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci /* Allocate a temporal buffer. */ 211162306a36Sopenharmony_ci d_security = kzalloc(aligned_sec_size, GFP_NOFS); 211262306a36Sopenharmony_ci if (!d_security) 211362306a36Sopenharmony_ci return -ENOMEM; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_SECURITY); 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci fnd_sdh = fnd_get(); 211862306a36Sopenharmony_ci if (!fnd_sdh) { 211962306a36Sopenharmony_ci err = -ENOMEM; 212062306a36Sopenharmony_ci goto out; 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci root_sdh = indx_get_root(indx_sdh, ni, NULL, NULL); 212462306a36Sopenharmony_ci if (!root_sdh) { 212562306a36Sopenharmony_ci err = -EINVAL; 212662306a36Sopenharmony_ci goto out; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci root_sii = indx_get_root(indx_sii, ni, NULL, NULL); 213062306a36Sopenharmony_ci if (!root_sii) { 213162306a36Sopenharmony_ci err = -EINVAL; 213262306a36Sopenharmony_ci goto out; 213362306a36Sopenharmony_ci } 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci /* 213662306a36Sopenharmony_ci * Check if such security already exists. 213762306a36Sopenharmony_ci * Use "SDH" and hash -> to get the offset in "SDS". 213862306a36Sopenharmony_ci */ 213962306a36Sopenharmony_ci err = indx_find(indx_sdh, ni, root_sdh, &hash_key, sizeof(hash_key), 214062306a36Sopenharmony_ci &d_security->key.sec_id, &diff, (struct NTFS_DE **)&e, 214162306a36Sopenharmony_ci fnd_sdh); 214262306a36Sopenharmony_ci if (err) 214362306a36Sopenharmony_ci goto out; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci while (e) { 214662306a36Sopenharmony_ci if (le32_to_cpu(e->sec_hdr.size) == new_sec_size) { 214762306a36Sopenharmony_ci err = ntfs_read_run_nb(sbi, &ni->file.run, 214862306a36Sopenharmony_ci le64_to_cpu(e->sec_hdr.off), 214962306a36Sopenharmony_ci d_security, new_sec_size, NULL); 215062306a36Sopenharmony_ci if (err) 215162306a36Sopenharmony_ci goto out; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci if (le32_to_cpu(d_security->size) == new_sec_size && 215462306a36Sopenharmony_ci d_security->key.hash == hash_key.hash && 215562306a36Sopenharmony_ci !memcmp(d_security + 1, sd, size_sd)) { 215662306a36Sopenharmony_ci *security_id = d_security->key.sec_id; 215762306a36Sopenharmony_ci /* Such security already exists. */ 215862306a36Sopenharmony_ci err = 0; 215962306a36Sopenharmony_ci goto out; 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci err = indx_find_sort(indx_sdh, ni, root_sdh, 216462306a36Sopenharmony_ci (struct NTFS_DE **)&e, fnd_sdh); 216562306a36Sopenharmony_ci if (err) 216662306a36Sopenharmony_ci goto out; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci if (!e || e->key.hash != hash_key.hash) 216962306a36Sopenharmony_ci break; 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci /* Zero unused space. */ 217362306a36Sopenharmony_ci next = sbi->security.next_off & (SecurityDescriptorsBlockSize - 1); 217462306a36Sopenharmony_ci left = SecurityDescriptorsBlockSize - next; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* Zero gap until SecurityDescriptorsBlockSize. */ 217762306a36Sopenharmony_ci if (left < new_sec_size) { 217862306a36Sopenharmony_ci /* Zero "left" bytes from sbi->security.next_off. */ 217962306a36Sopenharmony_ci sbi->security.next_off += SecurityDescriptorsBlockSize + left; 218062306a36Sopenharmony_ci } 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci /* Zero tail of previous security. */ 218362306a36Sopenharmony_ci //used = ni->vfs_inode.i_size & (SecurityDescriptorsBlockSize - 1); 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci /* 218662306a36Sopenharmony_ci * Example: 218762306a36Sopenharmony_ci * 0x40438 == ni->vfs_inode.i_size 218862306a36Sopenharmony_ci * 0x00440 == sbi->security.next_off 218962306a36Sopenharmony_ci * need to zero [0x438-0x440) 219062306a36Sopenharmony_ci * if (next > used) { 219162306a36Sopenharmony_ci * u32 tozero = next - used; 219262306a36Sopenharmony_ci * zero "tozero" bytes from sbi->security.next_off - tozero 219362306a36Sopenharmony_ci */ 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci /* Format new security descriptor. */ 219662306a36Sopenharmony_ci d_security->key.hash = hash_key.hash; 219762306a36Sopenharmony_ci d_security->key.sec_id = cpu_to_le32(sbi->security.next_id); 219862306a36Sopenharmony_ci d_security->off = cpu_to_le64(sbi->security.next_off); 219962306a36Sopenharmony_ci d_security->size = cpu_to_le32(new_sec_size); 220062306a36Sopenharmony_ci memcpy(d_security + 1, sd, size_sd); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci /* Write main SDS bucket. */ 220362306a36Sopenharmony_ci err = ntfs_sb_write_run(sbi, &ni->file.run, sbi->security.next_off, 220462306a36Sopenharmony_ci d_security, aligned_sec_size, 0); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci if (err) 220762306a36Sopenharmony_ci goto out; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci mirr_off = sbi->security.next_off + SecurityDescriptorsBlockSize; 221062306a36Sopenharmony_ci new_sds_size = mirr_off + aligned_sec_size; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci if (new_sds_size > ni->vfs_inode.i_size) { 221362306a36Sopenharmony_ci err = attr_set_size(ni, ATTR_DATA, SDS_NAME, 221462306a36Sopenharmony_ci ARRAY_SIZE(SDS_NAME), &ni->file.run, 221562306a36Sopenharmony_ci new_sds_size, &new_sds_size, false, NULL); 221662306a36Sopenharmony_ci if (err) 221762306a36Sopenharmony_ci goto out; 221862306a36Sopenharmony_ci } 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci /* Write copy SDS bucket. */ 222162306a36Sopenharmony_ci err = ntfs_sb_write_run(sbi, &ni->file.run, mirr_off, d_security, 222262306a36Sopenharmony_ci aligned_sec_size, 0); 222362306a36Sopenharmony_ci if (err) 222462306a36Sopenharmony_ci goto out; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci /* Fill SII entry. */ 222762306a36Sopenharmony_ci sii_e.de.view.data_off = 222862306a36Sopenharmony_ci cpu_to_le16(offsetof(struct NTFS_DE_SII, sec_hdr)); 222962306a36Sopenharmony_ci sii_e.de.view.data_size = cpu_to_le16(sizeof(struct SECURITY_HDR)); 223062306a36Sopenharmony_ci sii_e.de.view.res = 0; 223162306a36Sopenharmony_ci sii_e.de.size = cpu_to_le16(sizeof(struct NTFS_DE_SII)); 223262306a36Sopenharmony_ci sii_e.de.key_size = cpu_to_le16(sizeof(d_security->key.sec_id)); 223362306a36Sopenharmony_ci sii_e.de.flags = 0; 223462306a36Sopenharmony_ci sii_e.de.res = 0; 223562306a36Sopenharmony_ci sii_e.sec_id = d_security->key.sec_id; 223662306a36Sopenharmony_ci memcpy(&sii_e.sec_hdr, d_security, sizeof(struct SECURITY_HDR)); 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL, 0); 223962306a36Sopenharmony_ci if (err) 224062306a36Sopenharmony_ci goto out; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci /* Fill SDH entry. */ 224362306a36Sopenharmony_ci sdh_e.de.view.data_off = 224462306a36Sopenharmony_ci cpu_to_le16(offsetof(struct NTFS_DE_SDH, sec_hdr)); 224562306a36Sopenharmony_ci sdh_e.de.view.data_size = cpu_to_le16(sizeof(struct SECURITY_HDR)); 224662306a36Sopenharmony_ci sdh_e.de.view.res = 0; 224762306a36Sopenharmony_ci sdh_e.de.size = cpu_to_le16(SIZEOF_SDH_DIRENTRY); 224862306a36Sopenharmony_ci sdh_e.de.key_size = cpu_to_le16(sizeof(sdh_e.key)); 224962306a36Sopenharmony_ci sdh_e.de.flags = 0; 225062306a36Sopenharmony_ci sdh_e.de.res = 0; 225162306a36Sopenharmony_ci sdh_e.key.hash = d_security->key.hash; 225262306a36Sopenharmony_ci sdh_e.key.sec_id = d_security->key.sec_id; 225362306a36Sopenharmony_ci memcpy(&sdh_e.sec_hdr, d_security, sizeof(struct SECURITY_HDR)); 225462306a36Sopenharmony_ci sdh_e.magic[0] = cpu_to_le16('I'); 225562306a36Sopenharmony_ci sdh_e.magic[1] = cpu_to_le16('I'); 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci fnd_clear(fnd_sdh); 225862306a36Sopenharmony_ci err = indx_insert_entry(indx_sdh, ni, &sdh_e.de, (void *)(size_t)1, 225962306a36Sopenharmony_ci fnd_sdh, 0); 226062306a36Sopenharmony_ci if (err) 226162306a36Sopenharmony_ci goto out; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci *security_id = d_security->key.sec_id; 226462306a36Sopenharmony_ci if (inserted) 226562306a36Sopenharmony_ci *inserted = true; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci /* Update Id and offset for next descriptor. */ 226862306a36Sopenharmony_ci sbi->security.next_id += 1; 226962306a36Sopenharmony_ci sbi->security.next_off += aligned_sec_size; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ciout: 227262306a36Sopenharmony_ci fnd_put(fnd_sdh); 227362306a36Sopenharmony_ci mark_inode_dirty(&ni->vfs_inode); 227462306a36Sopenharmony_ci ni_unlock(ni); 227562306a36Sopenharmony_ci kfree(d_security); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci return err; 227862306a36Sopenharmony_ci} 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci/* 228162306a36Sopenharmony_ci * ntfs_reparse_init - Load and parse $Extend/$Reparse. 228262306a36Sopenharmony_ci */ 228362306a36Sopenharmony_ciint ntfs_reparse_init(struct ntfs_sb_info *sbi) 228462306a36Sopenharmony_ci{ 228562306a36Sopenharmony_ci int err; 228662306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->reparse.ni; 228762306a36Sopenharmony_ci struct ntfs_index *indx = &sbi->reparse.index_r; 228862306a36Sopenharmony_ci struct ATTRIB *attr; 228962306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le; 229062306a36Sopenharmony_ci const struct INDEX_ROOT *root_r; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci if (!ni) 229362306a36Sopenharmony_ci return 0; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci le = NULL; 229662306a36Sopenharmony_ci attr = ni_find_attr(ni, NULL, &le, ATTR_ROOT, SR_NAME, 229762306a36Sopenharmony_ci ARRAY_SIZE(SR_NAME), NULL, NULL); 229862306a36Sopenharmony_ci if (!attr) { 229962306a36Sopenharmony_ci err = -EINVAL; 230062306a36Sopenharmony_ci goto out; 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci root_r = resident_data(attr); 230462306a36Sopenharmony_ci if (root_r->type != ATTR_ZERO || 230562306a36Sopenharmony_ci root_r->rule != NTFS_COLLATION_TYPE_UINTS) { 230662306a36Sopenharmony_ci err = -EINVAL; 230762306a36Sopenharmony_ci goto out; 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci err = indx_init(indx, sbi, attr, INDEX_MUTEX_SR); 231162306a36Sopenharmony_ci if (err) 231262306a36Sopenharmony_ci goto out; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ciout: 231562306a36Sopenharmony_ci return err; 231662306a36Sopenharmony_ci} 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci/* 231962306a36Sopenharmony_ci * ntfs_objid_init - Load and parse $Extend/$ObjId. 232062306a36Sopenharmony_ci */ 232162306a36Sopenharmony_ciint ntfs_objid_init(struct ntfs_sb_info *sbi) 232262306a36Sopenharmony_ci{ 232362306a36Sopenharmony_ci int err; 232462306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->objid.ni; 232562306a36Sopenharmony_ci struct ntfs_index *indx = &sbi->objid.index_o; 232662306a36Sopenharmony_ci struct ATTRIB *attr; 232762306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le; 232862306a36Sopenharmony_ci const struct INDEX_ROOT *root; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci if (!ni) 233162306a36Sopenharmony_ci return 0; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci le = NULL; 233462306a36Sopenharmony_ci attr = ni_find_attr(ni, NULL, &le, ATTR_ROOT, SO_NAME, 233562306a36Sopenharmony_ci ARRAY_SIZE(SO_NAME), NULL, NULL); 233662306a36Sopenharmony_ci if (!attr) { 233762306a36Sopenharmony_ci err = -EINVAL; 233862306a36Sopenharmony_ci goto out; 233962306a36Sopenharmony_ci } 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci root = resident_data(attr); 234262306a36Sopenharmony_ci if (root->type != ATTR_ZERO || 234362306a36Sopenharmony_ci root->rule != NTFS_COLLATION_TYPE_UINTS) { 234462306a36Sopenharmony_ci err = -EINVAL; 234562306a36Sopenharmony_ci goto out; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci err = indx_init(indx, sbi, attr, INDEX_MUTEX_SO); 234962306a36Sopenharmony_ci if (err) 235062306a36Sopenharmony_ci goto out; 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ciout: 235362306a36Sopenharmony_ci return err; 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ciint ntfs_objid_remove(struct ntfs_sb_info *sbi, struct GUID *guid) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci int err; 235962306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->objid.ni; 236062306a36Sopenharmony_ci struct ntfs_index *indx = &sbi->objid.index_o; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci if (!ni) 236362306a36Sopenharmony_ci return -EINVAL; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_OBJID); 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci err = indx_delete_entry(indx, ni, guid, sizeof(*guid), NULL); 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci mark_inode_dirty(&ni->vfs_inode); 237062306a36Sopenharmony_ci ni_unlock(ni); 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci return err; 237362306a36Sopenharmony_ci} 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ciint ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag, 237662306a36Sopenharmony_ci const struct MFT_REF *ref) 237762306a36Sopenharmony_ci{ 237862306a36Sopenharmony_ci int err; 237962306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->reparse.ni; 238062306a36Sopenharmony_ci struct ntfs_index *indx = &sbi->reparse.index_r; 238162306a36Sopenharmony_ci struct NTFS_DE_R re; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci if (!ni) 238462306a36Sopenharmony_ci return -EINVAL; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci memset(&re, 0, sizeof(re)); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci re.de.view.data_off = cpu_to_le16(offsetof(struct NTFS_DE_R, zero)); 238962306a36Sopenharmony_ci re.de.size = cpu_to_le16(sizeof(struct NTFS_DE_R)); 239062306a36Sopenharmony_ci re.de.key_size = cpu_to_le16(sizeof(re.key)); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci re.key.ReparseTag = rtag; 239362306a36Sopenharmony_ci memcpy(&re.key.ref, ref, sizeof(*ref)); 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE); 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci err = indx_insert_entry(indx, ni, &re.de, NULL, NULL, 0); 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci mark_inode_dirty(&ni->vfs_inode); 240062306a36Sopenharmony_ci ni_unlock(ni); 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci return err; 240362306a36Sopenharmony_ci} 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ciint ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag, 240662306a36Sopenharmony_ci const struct MFT_REF *ref) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci int err, diff; 240962306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->reparse.ni; 241062306a36Sopenharmony_ci struct ntfs_index *indx = &sbi->reparse.index_r; 241162306a36Sopenharmony_ci struct ntfs_fnd *fnd = NULL; 241262306a36Sopenharmony_ci struct REPARSE_KEY rkey; 241362306a36Sopenharmony_ci struct NTFS_DE_R *re; 241462306a36Sopenharmony_ci struct INDEX_ROOT *root_r; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci if (!ni) 241762306a36Sopenharmony_ci return -EINVAL; 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci rkey.ReparseTag = rtag; 242062306a36Sopenharmony_ci rkey.ref = *ref; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci mutex_lock_nested(&ni->ni_lock, NTFS_INODE_MUTEX_REPARSE); 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci if (rtag) { 242562306a36Sopenharmony_ci err = indx_delete_entry(indx, ni, &rkey, sizeof(rkey), NULL); 242662306a36Sopenharmony_ci goto out1; 242762306a36Sopenharmony_ci } 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci fnd = fnd_get(); 243062306a36Sopenharmony_ci if (!fnd) { 243162306a36Sopenharmony_ci err = -ENOMEM; 243262306a36Sopenharmony_ci goto out1; 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci root_r = indx_get_root(indx, ni, NULL, NULL); 243662306a36Sopenharmony_ci if (!root_r) { 243762306a36Sopenharmony_ci err = -EINVAL; 243862306a36Sopenharmony_ci goto out; 243962306a36Sopenharmony_ci } 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci /* 1 - forces to ignore rkey.ReparseTag when comparing keys. */ 244262306a36Sopenharmony_ci err = indx_find(indx, ni, root_r, &rkey, sizeof(rkey), (void *)1, &diff, 244362306a36Sopenharmony_ci (struct NTFS_DE **)&re, fnd); 244462306a36Sopenharmony_ci if (err) 244562306a36Sopenharmony_ci goto out; 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci if (memcmp(&re->key.ref, ref, sizeof(*ref))) { 244862306a36Sopenharmony_ci /* Impossible. Looks like volume corrupt? */ 244962306a36Sopenharmony_ci goto out; 245062306a36Sopenharmony_ci } 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci memcpy(&rkey, &re->key, sizeof(rkey)); 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci fnd_put(fnd); 245562306a36Sopenharmony_ci fnd = NULL; 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci err = indx_delete_entry(indx, ni, &rkey, sizeof(rkey), NULL); 245862306a36Sopenharmony_ci if (err) 245962306a36Sopenharmony_ci goto out; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ciout: 246262306a36Sopenharmony_ci fnd_put(fnd); 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ciout1: 246562306a36Sopenharmony_ci mark_inode_dirty(&ni->vfs_inode); 246662306a36Sopenharmony_ci ni_unlock(ni); 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci return err; 246962306a36Sopenharmony_ci} 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_cistatic inline void ntfs_unmap_and_discard(struct ntfs_sb_info *sbi, CLST lcn, 247262306a36Sopenharmony_ci CLST len) 247362306a36Sopenharmony_ci{ 247462306a36Sopenharmony_ci ntfs_unmap_meta(sbi->sb, lcn, len); 247562306a36Sopenharmony_ci ntfs_discard(sbi, lcn, len); 247662306a36Sopenharmony_ci} 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_civoid mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim) 247962306a36Sopenharmony_ci{ 248062306a36Sopenharmony_ci CLST end, i, zone_len, zlen; 248162306a36Sopenharmony_ci struct wnd_bitmap *wnd = &sbi->used.bitmap; 248262306a36Sopenharmony_ci bool dirty = false; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci down_write_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS); 248562306a36Sopenharmony_ci if (!wnd_is_used(wnd, lcn, len)) { 248662306a36Sopenharmony_ci /* mark volume as dirty out of wnd->rw_lock */ 248762306a36Sopenharmony_ci dirty = true; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci end = lcn + len; 249062306a36Sopenharmony_ci len = 0; 249162306a36Sopenharmony_ci for (i = lcn; i < end; i++) { 249262306a36Sopenharmony_ci if (wnd_is_used(wnd, i, 1)) { 249362306a36Sopenharmony_ci if (!len) 249462306a36Sopenharmony_ci lcn = i; 249562306a36Sopenharmony_ci len += 1; 249662306a36Sopenharmony_ci continue; 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci if (!len) 250062306a36Sopenharmony_ci continue; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci if (trim) 250362306a36Sopenharmony_ci ntfs_unmap_and_discard(sbi, lcn, len); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci wnd_set_free(wnd, lcn, len); 250662306a36Sopenharmony_ci len = 0; 250762306a36Sopenharmony_ci } 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci if (!len) 251062306a36Sopenharmony_ci goto out; 251162306a36Sopenharmony_ci } 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci if (trim) 251462306a36Sopenharmony_ci ntfs_unmap_and_discard(sbi, lcn, len); 251562306a36Sopenharmony_ci wnd_set_free(wnd, lcn, len); 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci /* append to MFT zone, if possible. */ 251862306a36Sopenharmony_ci zone_len = wnd_zone_len(wnd); 251962306a36Sopenharmony_ci zlen = min(zone_len + len, sbi->zone_max); 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci if (zlen == zone_len) { 252262306a36Sopenharmony_ci /* MFT zone already has maximum size. */ 252362306a36Sopenharmony_ci } else if (!zone_len) { 252462306a36Sopenharmony_ci /* Create MFT zone only if 'zlen' is large enough. */ 252562306a36Sopenharmony_ci if (zlen == sbi->zone_max) 252662306a36Sopenharmony_ci wnd_zone_set(wnd, lcn, zlen); 252762306a36Sopenharmony_ci } else { 252862306a36Sopenharmony_ci CLST zone_lcn = wnd_zone_bit(wnd); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci if (lcn + len == zone_lcn) { 253162306a36Sopenharmony_ci /* Append into head MFT zone. */ 253262306a36Sopenharmony_ci wnd_zone_set(wnd, lcn, zlen); 253362306a36Sopenharmony_ci } else if (zone_lcn + zone_len == lcn) { 253462306a36Sopenharmony_ci /* Append into tail MFT zone. */ 253562306a36Sopenharmony_ci wnd_zone_set(wnd, zone_lcn, zlen); 253662306a36Sopenharmony_ci } 253762306a36Sopenharmony_ci } 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ciout: 254062306a36Sopenharmony_ci up_write(&wnd->rw_lock); 254162306a36Sopenharmony_ci if (dirty) 254262306a36Sopenharmony_ci ntfs_set_state(sbi, NTFS_DIRTY_ERROR); 254362306a36Sopenharmony_ci} 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci/* 254662306a36Sopenharmony_ci * run_deallocate - Deallocate clusters. 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ciint run_deallocate(struct ntfs_sb_info *sbi, const struct runs_tree *run, 254962306a36Sopenharmony_ci bool trim) 255062306a36Sopenharmony_ci{ 255162306a36Sopenharmony_ci CLST lcn, len; 255262306a36Sopenharmony_ci size_t idx = 0; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci while (run_get_entry(run, idx++, NULL, &lcn, &len)) { 255562306a36Sopenharmony_ci if (lcn == SPARSE_LCN) 255662306a36Sopenharmony_ci continue; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci mark_as_free_ex(sbi, lcn, len, trim); 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci return 0; 256262306a36Sopenharmony_ci} 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_cistatic inline bool name_has_forbidden_chars(const struct le_str *fname) 256562306a36Sopenharmony_ci{ 256662306a36Sopenharmony_ci int i, ch; 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci /* check for forbidden chars */ 256962306a36Sopenharmony_ci for (i = 0; i < fname->len; ++i) { 257062306a36Sopenharmony_ci ch = le16_to_cpu(fname->name[i]); 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci /* control chars */ 257362306a36Sopenharmony_ci if (ch < 0x20) 257462306a36Sopenharmony_ci return true; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci switch (ch) { 257762306a36Sopenharmony_ci /* disallowed by Windows */ 257862306a36Sopenharmony_ci case '\\': 257962306a36Sopenharmony_ci case '/': 258062306a36Sopenharmony_ci case ':': 258162306a36Sopenharmony_ci case '*': 258262306a36Sopenharmony_ci case '?': 258362306a36Sopenharmony_ci case '<': 258462306a36Sopenharmony_ci case '>': 258562306a36Sopenharmony_ci case '|': 258662306a36Sopenharmony_ci case '\"': 258762306a36Sopenharmony_ci return true; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci default: 259062306a36Sopenharmony_ci /* allowed char */ 259162306a36Sopenharmony_ci break; 259262306a36Sopenharmony_ci } 259362306a36Sopenharmony_ci } 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci /* file names cannot end with space or . */ 259662306a36Sopenharmony_ci if (fname->len > 0) { 259762306a36Sopenharmony_ci ch = le16_to_cpu(fname->name[fname->len - 1]); 259862306a36Sopenharmony_ci if (ch == ' ' || ch == '.') 259962306a36Sopenharmony_ci return true; 260062306a36Sopenharmony_ci } 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci return false; 260362306a36Sopenharmony_ci} 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_cistatic inline bool is_reserved_name(const struct ntfs_sb_info *sbi, 260662306a36Sopenharmony_ci const struct le_str *fname) 260762306a36Sopenharmony_ci{ 260862306a36Sopenharmony_ci int port_digit; 260962306a36Sopenharmony_ci const __le16 *name = fname->name; 261062306a36Sopenharmony_ci int len = fname->len; 261162306a36Sopenharmony_ci const u16 *upcase = sbi->upcase; 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci /* check for 3 chars reserved names (device names) */ 261462306a36Sopenharmony_ci /* name by itself or with any extension is forbidden */ 261562306a36Sopenharmony_ci if (len == 3 || (len > 3 && le16_to_cpu(name[3]) == '.')) 261662306a36Sopenharmony_ci if (!ntfs_cmp_names(name, 3, CON_NAME, 3, upcase, false) || 261762306a36Sopenharmony_ci !ntfs_cmp_names(name, 3, NUL_NAME, 3, upcase, false) || 261862306a36Sopenharmony_ci !ntfs_cmp_names(name, 3, AUX_NAME, 3, upcase, false) || 261962306a36Sopenharmony_ci !ntfs_cmp_names(name, 3, PRN_NAME, 3, upcase, false)) 262062306a36Sopenharmony_ci return true; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci /* check for 4 chars reserved names (port name followed by 1..9) */ 262362306a36Sopenharmony_ci /* name by itself or with any extension is forbidden */ 262462306a36Sopenharmony_ci if (len == 4 || (len > 4 && le16_to_cpu(name[4]) == '.')) { 262562306a36Sopenharmony_ci port_digit = le16_to_cpu(name[3]); 262662306a36Sopenharmony_ci if (port_digit >= '1' && port_digit <= '9') 262762306a36Sopenharmony_ci if (!ntfs_cmp_names(name, 3, COM_NAME, 3, upcase, 262862306a36Sopenharmony_ci false) || 262962306a36Sopenharmony_ci !ntfs_cmp_names(name, 3, LPT_NAME, 3, upcase, 263062306a36Sopenharmony_ci false)) 263162306a36Sopenharmony_ci return true; 263262306a36Sopenharmony_ci } 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci return false; 263562306a36Sopenharmony_ci} 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci/* 263862306a36Sopenharmony_ci * valid_windows_name - Check if a file name is valid in Windows. 263962306a36Sopenharmony_ci */ 264062306a36Sopenharmony_cibool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str *fname) 264162306a36Sopenharmony_ci{ 264262306a36Sopenharmony_ci return !name_has_forbidden_chars(fname) && 264362306a36Sopenharmony_ci !is_reserved_name(sbi, fname); 264462306a36Sopenharmony_ci} 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci/* 264762306a36Sopenharmony_ci * ntfs_set_label - updates current ntfs label. 264862306a36Sopenharmony_ci */ 264962306a36Sopenharmony_ciint ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len) 265062306a36Sopenharmony_ci{ 265162306a36Sopenharmony_ci int err; 265262306a36Sopenharmony_ci struct ATTRIB *attr; 265362306a36Sopenharmony_ci struct ntfs_inode *ni = sbi->volume.ni; 265462306a36Sopenharmony_ci const u8 max_ulen = 0x80; /* TODO: use attrdef to get maximum length */ 265562306a36Sopenharmony_ci /* Allocate PATH_MAX bytes. */ 265662306a36Sopenharmony_ci struct cpu_str *uni = __getname(); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci if (!uni) 265962306a36Sopenharmony_ci return -ENOMEM; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci err = ntfs_nls_to_utf16(sbi, label, len, uni, (PATH_MAX - 2) / 2, 266262306a36Sopenharmony_ci UTF16_LITTLE_ENDIAN); 266362306a36Sopenharmony_ci if (err < 0) 266462306a36Sopenharmony_ci goto out; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci if (uni->len > max_ulen) { 266762306a36Sopenharmony_ci ntfs_warn(sbi->sb, "new label is too long"); 266862306a36Sopenharmony_ci err = -EFBIG; 266962306a36Sopenharmony_ci goto out; 267062306a36Sopenharmony_ci } 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci ni_lock(ni); 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci /* Ignore any errors. */ 267562306a36Sopenharmony_ci ni_remove_attr(ni, ATTR_LABEL, NULL, 0, false, NULL); 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci err = ni_insert_resident(ni, uni->len * sizeof(u16), ATTR_LABEL, NULL, 267862306a36Sopenharmony_ci 0, &attr, NULL, NULL); 267962306a36Sopenharmony_ci if (err < 0) 268062306a36Sopenharmony_ci goto unlock_out; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci /* write new label in on-disk struct. */ 268362306a36Sopenharmony_ci memcpy(resident_data(attr), uni->name, uni->len * sizeof(u16)); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci /* update cached value of current label. */ 268662306a36Sopenharmony_ci if (len >= ARRAY_SIZE(sbi->volume.label)) 268762306a36Sopenharmony_ci len = ARRAY_SIZE(sbi->volume.label) - 1; 268862306a36Sopenharmony_ci memcpy(sbi->volume.label, label, len); 268962306a36Sopenharmony_ci sbi->volume.label[len] = 0; 269062306a36Sopenharmony_ci mark_inode_dirty_sync(&ni->vfs_inode); 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ciunlock_out: 269362306a36Sopenharmony_ci ni_unlock(ni); 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci if (!err) 269662306a36Sopenharmony_ci err = _ni_write_inode(&ni->vfs_inode, 0); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ciout: 269962306a36Sopenharmony_ci __putname(uni); 270062306a36Sopenharmony_ci return err; 270162306a36Sopenharmony_ci}