1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * NTFS multi sector transfer protection handling code. 4 * 5 * Copyright (c) 2001-2004 Anton Altaparmakov 6 */ 7 8 #include <linux/ratelimit.h> 9 10 #include "ntfs.h" 11 12 /* 13 * post_read_mst_fixup - deprotect multi sector transfer protected data 14 * @b: pointer to the data to deprotect 15 * @size: size in bytes of @b 16 * 17 * Perform the necessary post read multi sector transfer fixup and detect the 18 * presence of incomplete multi sector transfers. - In that case, overwrite the 19 * magic of the ntfs record header being processed with "BAAD" (in memory only!) 20 * and abort processing. 21 * 22 * Return 0 on success and -EINVAL on error ("BAAD" magic will be present). 23 * 24 * NOTE: We consider the absence / invalidity of an update sequence array to 25 * mean that the structure is not protected at all and hence doesn't need to 26 * be fixed up. Thus, we return success and not failure in this case. This is 27 * in contrast to pre_write_mst_fixup(), see below. 28 */ 29 int post_read_mst_fixup(struct ntfs_record *b, const u32 size) 30 { 31 u16 usa_ofs, usa_count, usn; 32 u16 *usa_pos, *data_pos; 33 34 /* Setup the variables. */ 35 usa_ofs = le16_to_cpu(b->usa_ofs); 36 /* Decrement usa_count to get number of fixups. */ 37 usa_count = le16_to_cpu(b->usa_count) - 1; 38 /* Size and alignment checks. */ 39 if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 40 usa_ofs + (usa_count * 2) > size || 41 (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) 42 return 0; 43 /* Position of usn in update sequence array. */ 44 usa_pos = (u16 *)b + usa_ofs/sizeof(u16); 45 /* 46 * The update sequence number which has to be equal to each of the 47 * u16 values before they are fixed up. Note no need to care for 48 * endianness since we are comparing and moving data for on disk 49 * structures which means the data is consistent. - If it is 50 * consistenty the wrong endianness it doesn't make any difference. 51 */ 52 usn = *usa_pos; 53 /* 54 * Position in protected data of first u16 that needs fixing up. 55 */ 56 data_pos = (u16 *)b + NTFS_BLOCK_SIZE / sizeof(u16) - 1; 57 /* 58 * Check for incomplete multi sector transfer(s). 59 */ 60 while (usa_count--) { 61 if (*data_pos != usn) { 62 struct mft_record *m = (struct mft_record *)b; 63 64 pr_err_ratelimited("ntfs: Incomplete multi sector transfer detected! (Record magic : 0x%x, mft number : 0x%x, base mft number : 0x%lx, mft in use : %d, data : 0x%x, usn 0x%x)\n", 65 le32_to_cpu(m->magic), le32_to_cpu(m->mft_record_number), 66 MREF_LE(m->base_mft_record), m->flags & MFT_RECORD_IN_USE, 67 *data_pos, usn); 68 /* 69 * Incomplete multi sector transfer detected! )-: 70 * Set the magic to "BAAD" and return failure. 71 * Note that magic_BAAD is already converted to le32. 72 */ 73 b->magic = magic_BAAD; 74 return -EINVAL; 75 } 76 data_pos += NTFS_BLOCK_SIZE / sizeof(u16); 77 } 78 /* Re-setup the variables. */ 79 usa_count = le16_to_cpu(b->usa_count) - 1; 80 data_pos = (u16 *)b + NTFS_BLOCK_SIZE / sizeof(u16) - 1; 81 /* Fixup all sectors. */ 82 while (usa_count--) { 83 /* 84 * Increment position in usa and restore original data from 85 * the usa into the data buffer. 86 */ 87 *data_pos = *(++usa_pos); 88 /* Increment position in data as well. */ 89 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 90 } 91 return 0; 92 } 93 94 /* 95 * pre_write_mst_fixup - apply multi sector transfer protection 96 * @b: pointer to the data to protect 97 * @size: size in bytes of @b 98 * 99 * Perform the necessary pre write multi sector transfer fixup on the data 100 * pointer to by @b of @size. 101 * 102 * Return 0 if fixup applied (success) or -EINVAL if no fixup was performed 103 * (assumed not needed). This is in contrast to post_read_mst_fixup() above. 104 * 105 * NOTE: We consider the absence / invalidity of an update sequence array to 106 * mean that the structure is not subject to protection and hence doesn't need 107 * to be fixed up. This means that you have to create a valid update sequence 108 * array header in the ntfs record before calling this function, otherwise it 109 * will fail (the header needs to contain the position of the update sequence 110 * array together with the number of elements in the array). You also need to 111 * initialise the update sequence number before calling this function 112 * otherwise a random word will be used (whatever was in the record at that 113 * position at that time). 114 */ 115 int pre_write_mst_fixup(struct ntfs_record *b, const u32 size) 116 { 117 __le16 *usa_pos, *data_pos; 118 u16 usa_ofs, usa_count, usn; 119 __le16 le_usn; 120 121 /* Sanity check + only fixup if it makes sense. */ 122 if (!b || ntfs_is_baad_record(b->magic) || 123 ntfs_is_hole_record(b->magic)) 124 return -EINVAL; 125 /* Setup the variables. */ 126 usa_ofs = le16_to_cpu(b->usa_ofs); 127 /* Decrement usa_count to get number of fixups. */ 128 usa_count = le16_to_cpu(b->usa_count) - 1; 129 /* Size and alignment checks. */ 130 if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 131 usa_ofs + (usa_count * 2) > size || 132 (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) 133 return -EINVAL; 134 /* Position of usn in update sequence array. */ 135 usa_pos = (__le16 *)((u8 *)b + usa_ofs); 136 /* 137 * Cyclically increment the update sequence number 138 * (skipping 0 and -1, i.e. 0xffff). 139 */ 140 usn = le16_to_cpup(usa_pos) + 1; 141 if (usn == 0xffff || !usn) 142 usn = 1; 143 le_usn = cpu_to_le16(usn); 144 *usa_pos = le_usn; 145 /* Position in data of first u16 that needs fixing up. */ 146 data_pos = (__le16 *)b + NTFS_BLOCK_SIZE/sizeof(__le16) - 1; 147 /* Fixup all sectors. */ 148 while (usa_count--) { 149 /* 150 * Increment the position in the usa and save the 151 * original data from the data buffer into the usa. 152 */ 153 *(++usa_pos) = *data_pos; 154 /* Apply fixup to data. */ 155 *data_pos = le_usn; 156 /* Increment position in data as well. */ 157 data_pos += NTFS_BLOCK_SIZE / sizeof(__le16); 158 } 159 return 0; 160 } 161 162 /* 163 * post_write_mst_fixup - fast deprotect multi sector transfer protected data 164 * @b: pointer to the data to deprotect 165 * 166 * Perform the necessary post write multi sector transfer fixup, not checking 167 * for any errors, because we assume we have just used pre_write_mst_fixup(), 168 * thus the data will be fine or we would never have gotten here. 169 */ 170 void post_write_mst_fixup(struct ntfs_record *b) 171 { 172 __le16 *usa_pos, *data_pos; 173 174 u16 usa_ofs = le16_to_cpu(b->usa_ofs); 175 u16 usa_count = le16_to_cpu(b->usa_count) - 1; 176 177 /* Position of usn in update sequence array. */ 178 usa_pos = (__le16 *)b + usa_ofs/sizeof(__le16); 179 180 /* Position in protected data of first u16 that needs fixing up. */ 181 data_pos = (__le16 *)b + NTFS_BLOCK_SIZE/sizeof(__le16) - 1; 182 183 /* Fixup all sectors. */ 184 while (usa_count--) { 185 /* 186 * Increment position in usa and restore original data from 187 * the usa into the data buffer. 188 */ 189 *data_pos = *(++usa_pos); 190 191 /* Increment position in data as well. */ 192 data_pos += NTFS_BLOCK_SIZE/sizeof(__le16); 193 } 194 } 195