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