11e9ea7e0SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later 21e9ea7e0SNamjae Jeon /* 3*115380f9SNamjae Jeon * NTFS multi sector transfer protection handling code. 41e9ea7e0SNamjae Jeon * 51e9ea7e0SNamjae Jeon * Copyright (c) 2001-2004 Anton Altaparmakov 61e9ea7e0SNamjae Jeon */ 71e9ea7e0SNamjae Jeon 8*115380f9SNamjae Jeon #include <linux/ratelimit.h> 9*115380f9SNamjae Jeon 101e9ea7e0SNamjae Jeon #include "ntfs.h" 111e9ea7e0SNamjae Jeon 12*115380f9SNamjae Jeon /* 131e9ea7e0SNamjae Jeon * post_read_mst_fixup - deprotect multi sector transfer protected data 141e9ea7e0SNamjae Jeon * @b: pointer to the data to deprotect 151e9ea7e0SNamjae Jeon * @size: size in bytes of @b 161e9ea7e0SNamjae Jeon * 171e9ea7e0SNamjae Jeon * Perform the necessary post read multi sector transfer fixup and detect the 181e9ea7e0SNamjae Jeon * presence of incomplete multi sector transfers. - In that case, overwrite the 191e9ea7e0SNamjae Jeon * magic of the ntfs record header being processed with "BAAD" (in memory only!) 201e9ea7e0SNamjae Jeon * and abort processing. 211e9ea7e0SNamjae Jeon * 221e9ea7e0SNamjae Jeon * Return 0 on success and -EINVAL on error ("BAAD" magic will be present). 231e9ea7e0SNamjae Jeon * 241e9ea7e0SNamjae Jeon * NOTE: We consider the absence / invalidity of an update sequence array to 251e9ea7e0SNamjae Jeon * mean that the structure is not protected at all and hence doesn't need to 261e9ea7e0SNamjae Jeon * be fixed up. Thus, we return success and not failure in this case. This is 271e9ea7e0SNamjae Jeon * in contrast to pre_write_mst_fixup(), see below. 281e9ea7e0SNamjae Jeon */ 29*115380f9SNamjae Jeon int post_read_mst_fixup(struct ntfs_record *b, const u32 size) 301e9ea7e0SNamjae Jeon { 311e9ea7e0SNamjae Jeon u16 usa_ofs, usa_count, usn; 321e9ea7e0SNamjae Jeon u16 *usa_pos, *data_pos; 331e9ea7e0SNamjae Jeon 341e9ea7e0SNamjae Jeon /* Setup the variables. */ 351e9ea7e0SNamjae Jeon usa_ofs = le16_to_cpu(b->usa_ofs); 361e9ea7e0SNamjae Jeon /* Decrement usa_count to get number of fixups. */ 371e9ea7e0SNamjae Jeon usa_count = le16_to_cpu(b->usa_count) - 1; 381e9ea7e0SNamjae Jeon /* Size and alignment checks. */ 39*115380f9SNamjae Jeon if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 401e9ea7e0SNamjae Jeon usa_ofs + (usa_count * 2) > size || 411e9ea7e0SNamjae Jeon (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) 421e9ea7e0SNamjae Jeon return 0; 431e9ea7e0SNamjae Jeon /* Position of usn in update sequence array. */ 441e9ea7e0SNamjae Jeon usa_pos = (u16 *)b + usa_ofs/sizeof(u16); 451e9ea7e0SNamjae Jeon /* 461e9ea7e0SNamjae Jeon * The update sequence number which has to be equal to each of the 471e9ea7e0SNamjae Jeon * u16 values before they are fixed up. Note no need to care for 481e9ea7e0SNamjae Jeon * endianness since we are comparing and moving data for on disk 491e9ea7e0SNamjae Jeon * structures which means the data is consistent. - If it is 501e9ea7e0SNamjae Jeon * consistenty the wrong endianness it doesn't make any difference. 511e9ea7e0SNamjae Jeon */ 521e9ea7e0SNamjae Jeon usn = *usa_pos; 531e9ea7e0SNamjae Jeon /* 541e9ea7e0SNamjae Jeon * Position in protected data of first u16 that needs fixing up. 551e9ea7e0SNamjae Jeon */ 561e9ea7e0SNamjae Jeon data_pos = (u16 *)b + NTFS_BLOCK_SIZE / sizeof(u16) - 1; 571e9ea7e0SNamjae Jeon /* 581e9ea7e0SNamjae Jeon * Check for incomplete multi sector transfer(s). 591e9ea7e0SNamjae Jeon */ 601e9ea7e0SNamjae Jeon while (usa_count--) { 611e9ea7e0SNamjae Jeon if (*data_pos != usn) { 62*115380f9SNamjae Jeon struct mft_record *m = (struct mft_record *)b; 63*115380f9SNamjae Jeon 64*115380f9SNamjae Jeon 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*115380f9SNamjae Jeon le32_to_cpu(m->magic), le32_to_cpu(m->mft_record_number), 66*115380f9SNamjae Jeon MREF_LE(m->base_mft_record), m->flags & MFT_RECORD_IN_USE, 67*115380f9SNamjae Jeon *data_pos, usn); 681e9ea7e0SNamjae Jeon /* 691e9ea7e0SNamjae Jeon * Incomplete multi sector transfer detected! )-: 701e9ea7e0SNamjae Jeon * Set the magic to "BAAD" and return failure. 711e9ea7e0SNamjae Jeon * Note that magic_BAAD is already converted to le32. 721e9ea7e0SNamjae Jeon */ 731e9ea7e0SNamjae Jeon b->magic = magic_BAAD; 741e9ea7e0SNamjae Jeon return -EINVAL; 751e9ea7e0SNamjae Jeon } 761e9ea7e0SNamjae Jeon data_pos += NTFS_BLOCK_SIZE / sizeof(u16); 771e9ea7e0SNamjae Jeon } 781e9ea7e0SNamjae Jeon /* Re-setup the variables. */ 791e9ea7e0SNamjae Jeon usa_count = le16_to_cpu(b->usa_count) - 1; 801e9ea7e0SNamjae Jeon data_pos = (u16 *)b + NTFS_BLOCK_SIZE / sizeof(u16) - 1; 811e9ea7e0SNamjae Jeon /* Fixup all sectors. */ 821e9ea7e0SNamjae Jeon while (usa_count--) { 831e9ea7e0SNamjae Jeon /* 841e9ea7e0SNamjae Jeon * Increment position in usa and restore original data from 851e9ea7e0SNamjae Jeon * the usa into the data buffer. 861e9ea7e0SNamjae Jeon */ 871e9ea7e0SNamjae Jeon *data_pos = *(++usa_pos); 881e9ea7e0SNamjae Jeon /* Increment position in data as well. */ 891e9ea7e0SNamjae Jeon data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 901e9ea7e0SNamjae Jeon } 911e9ea7e0SNamjae Jeon return 0; 921e9ea7e0SNamjae Jeon } 931e9ea7e0SNamjae Jeon 94*115380f9SNamjae Jeon /* 951e9ea7e0SNamjae Jeon * pre_write_mst_fixup - apply multi sector transfer protection 961e9ea7e0SNamjae Jeon * @b: pointer to the data to protect 971e9ea7e0SNamjae Jeon * @size: size in bytes of @b 981e9ea7e0SNamjae Jeon * 991e9ea7e0SNamjae Jeon * Perform the necessary pre write multi sector transfer fixup on the data 1001e9ea7e0SNamjae Jeon * pointer to by @b of @size. 1011e9ea7e0SNamjae Jeon * 1021e9ea7e0SNamjae Jeon * Return 0 if fixup applied (success) or -EINVAL if no fixup was performed 1031e9ea7e0SNamjae Jeon * (assumed not needed). This is in contrast to post_read_mst_fixup() above. 1041e9ea7e0SNamjae Jeon * 1051e9ea7e0SNamjae Jeon * NOTE: We consider the absence / invalidity of an update sequence array to 1061e9ea7e0SNamjae Jeon * mean that the structure is not subject to protection and hence doesn't need 1071e9ea7e0SNamjae Jeon * to be fixed up. This means that you have to create a valid update sequence 1081e9ea7e0SNamjae Jeon * array header in the ntfs record before calling this function, otherwise it 1091e9ea7e0SNamjae Jeon * will fail (the header needs to contain the position of the update sequence 1101e9ea7e0SNamjae Jeon * array together with the number of elements in the array). You also need to 1111e9ea7e0SNamjae Jeon * initialise the update sequence number before calling this function 1121e9ea7e0SNamjae Jeon * otherwise a random word will be used (whatever was in the record at that 1131e9ea7e0SNamjae Jeon * position at that time). 1141e9ea7e0SNamjae Jeon */ 115*115380f9SNamjae Jeon int pre_write_mst_fixup(struct ntfs_record *b, const u32 size) 1161e9ea7e0SNamjae Jeon { 117*115380f9SNamjae Jeon __le16 *usa_pos, *data_pos; 1181e9ea7e0SNamjae Jeon u16 usa_ofs, usa_count, usn; 119*115380f9SNamjae Jeon __le16 le_usn; 1201e9ea7e0SNamjae Jeon 1211e9ea7e0SNamjae Jeon /* Sanity check + only fixup if it makes sense. */ 1221e9ea7e0SNamjae Jeon if (!b || ntfs_is_baad_record(b->magic) || 1231e9ea7e0SNamjae Jeon ntfs_is_hole_record(b->magic)) 1241e9ea7e0SNamjae Jeon return -EINVAL; 1251e9ea7e0SNamjae Jeon /* Setup the variables. */ 1261e9ea7e0SNamjae Jeon usa_ofs = le16_to_cpu(b->usa_ofs); 1271e9ea7e0SNamjae Jeon /* Decrement usa_count to get number of fixups. */ 1281e9ea7e0SNamjae Jeon usa_count = le16_to_cpu(b->usa_count) - 1; 1291e9ea7e0SNamjae Jeon /* Size and alignment checks. */ 130*115380f9SNamjae Jeon if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 1311e9ea7e0SNamjae Jeon usa_ofs + (usa_count * 2) > size || 1321e9ea7e0SNamjae Jeon (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) 1331e9ea7e0SNamjae Jeon return -EINVAL; 1341e9ea7e0SNamjae Jeon /* Position of usn in update sequence array. */ 135*115380f9SNamjae Jeon usa_pos = (__le16 *)((u8 *)b + usa_ofs); 1361e9ea7e0SNamjae Jeon /* 1371e9ea7e0SNamjae Jeon * Cyclically increment the update sequence number 1381e9ea7e0SNamjae Jeon * (skipping 0 and -1, i.e. 0xffff). 1391e9ea7e0SNamjae Jeon */ 1401e9ea7e0SNamjae Jeon usn = le16_to_cpup(usa_pos) + 1; 1411e9ea7e0SNamjae Jeon if (usn == 0xffff || !usn) 1421e9ea7e0SNamjae Jeon usn = 1; 1431e9ea7e0SNamjae Jeon le_usn = cpu_to_le16(usn); 1441e9ea7e0SNamjae Jeon *usa_pos = le_usn; 1451e9ea7e0SNamjae Jeon /* Position in data of first u16 that needs fixing up. */ 146*115380f9SNamjae Jeon data_pos = (__le16 *)b + NTFS_BLOCK_SIZE/sizeof(__le16) - 1; 1471e9ea7e0SNamjae Jeon /* Fixup all sectors. */ 1481e9ea7e0SNamjae Jeon while (usa_count--) { 1491e9ea7e0SNamjae Jeon /* 1501e9ea7e0SNamjae Jeon * Increment the position in the usa and save the 1511e9ea7e0SNamjae Jeon * original data from the data buffer into the usa. 1521e9ea7e0SNamjae Jeon */ 1531e9ea7e0SNamjae Jeon *(++usa_pos) = *data_pos; 1541e9ea7e0SNamjae Jeon /* Apply fixup to data. */ 1551e9ea7e0SNamjae Jeon *data_pos = le_usn; 1561e9ea7e0SNamjae Jeon /* Increment position in data as well. */ 157*115380f9SNamjae Jeon data_pos += NTFS_BLOCK_SIZE / sizeof(__le16); 1581e9ea7e0SNamjae Jeon } 1591e9ea7e0SNamjae Jeon return 0; 1601e9ea7e0SNamjae Jeon } 1611e9ea7e0SNamjae Jeon 162*115380f9SNamjae Jeon /* 1631e9ea7e0SNamjae Jeon * post_write_mst_fixup - fast deprotect multi sector transfer protected data 1641e9ea7e0SNamjae Jeon * @b: pointer to the data to deprotect 1651e9ea7e0SNamjae Jeon * 1661e9ea7e0SNamjae Jeon * Perform the necessary post write multi sector transfer fixup, not checking 1671e9ea7e0SNamjae Jeon * for any errors, because we assume we have just used pre_write_mst_fixup(), 1681e9ea7e0SNamjae Jeon * thus the data will be fine or we would never have gotten here. 1691e9ea7e0SNamjae Jeon */ 170*115380f9SNamjae Jeon void post_write_mst_fixup(struct ntfs_record *b) 1711e9ea7e0SNamjae Jeon { 172*115380f9SNamjae Jeon __le16 *usa_pos, *data_pos; 1731e9ea7e0SNamjae Jeon 1741e9ea7e0SNamjae Jeon u16 usa_ofs = le16_to_cpu(b->usa_ofs); 1751e9ea7e0SNamjae Jeon u16 usa_count = le16_to_cpu(b->usa_count) - 1; 1761e9ea7e0SNamjae Jeon 1771e9ea7e0SNamjae Jeon /* Position of usn in update sequence array. */ 178*115380f9SNamjae Jeon usa_pos = (__le16 *)b + usa_ofs/sizeof(__le16); 1791e9ea7e0SNamjae Jeon 1801e9ea7e0SNamjae Jeon /* Position in protected data of first u16 that needs fixing up. */ 181*115380f9SNamjae Jeon data_pos = (__le16 *)b + NTFS_BLOCK_SIZE/sizeof(__le16) - 1; 1821e9ea7e0SNamjae Jeon 1831e9ea7e0SNamjae Jeon /* Fixup all sectors. */ 1841e9ea7e0SNamjae Jeon while (usa_count--) { 1851e9ea7e0SNamjae Jeon /* 1861e9ea7e0SNamjae Jeon * Increment position in usa and restore original data from 1871e9ea7e0SNamjae Jeon * the usa into the data buffer. 1881e9ea7e0SNamjae Jeon */ 1891e9ea7e0SNamjae Jeon *data_pos = *(++usa_pos); 1901e9ea7e0SNamjae Jeon 1911e9ea7e0SNamjae Jeon /* Increment position in data as well. */ 192*115380f9SNamjae Jeon data_pos += NTFS_BLOCK_SIZE/sizeof(__le16); 1931e9ea7e0SNamjae Jeon } 1941e9ea7e0SNamjae Jeon } 195