1 /** 2 * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. 3 * 4 * Copyright (c) 2000-2004 Anton Altaparmakov 5 * Copyright (c) 2006 Szabolcs Szakacsits 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 NTFS-3G 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 #ifdef HAVE_CONFIG_H 24 #include "config.h" 25 #endif 26 27 #ifdef HAVE_ERRNO_H 28 #include <errno.h> 29 #endif 30 31 #include "mst.h" 32 #include "logging.h" 33 34 /** 35 * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data 36 * @b: pointer to the data to deprotect 37 * @size: size in bytes of @b 38 * 39 * Perform the necessary post read multi sector transfer fixups and detect the 40 * presence of incomplete multi sector transfers. - In that case, overwrite the 41 * magic of the ntfs record header being processed with "BAAD" (in memory only!) 42 * and abort processing. 43 * 44 * Return 0 on success and -1 on error, with errno set to the error code. The 45 * following error codes are defined: 46 * EINVAL Invalid arguments or invalid NTFS record in buffer @b. 47 * EIO Multi sector transfer error was detected. Magic of the NTFS 48 * record in @b will have been set to "BAAD". 49 */ 50 int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) 51 { 52 u16 usa_ofs, usa_count, usn; 53 u16 *usa_pos, *data_pos; 54 55 ntfs_log_trace("Entering\n"); 56 57 /* Setup the variables. */ 58 usa_ofs = le16_to_cpu(b->usa_ofs); 59 /* Decrement usa_count to get number of fixups. */ 60 usa_count = le16_to_cpu(b->usa_count) - 1; 61 /* Size and alignment checks. */ 62 if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 63 (u32)(usa_ofs + (usa_count * 2)) > size || 64 (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { 65 errno = EINVAL; 66 return -1; 67 } 68 /* Position of usn in update sequence array. */ 69 usa_pos = (u16*)b + usa_ofs/sizeof(u16); 70 /* 71 * The update sequence number which has to be equal to each of the 72 * u16 values before they are fixed up. Note no need to care for 73 * endianness since we are comparing and moving data for on disk 74 * structures which means the data is consistent. - If it is 75 * consistency the wrong endianness it doesn't make any difference. 76 */ 77 usn = *usa_pos; 78 /* 79 * Position in protected data of first u16 that needs fixing up. 80 */ 81 data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 82 /* 83 * Check for incomplete multi sector transfer(s). 84 */ 85 while (usa_count--) { 86 if (*data_pos != usn) { 87 /* 88 * Incomplete multi sector transfer detected! )-: 89 * Set the magic to "BAAD" and return failure. 90 * Note that magic_BAAD is already converted to le32. 91 */ 92 b->magic = magic_BAAD; 93 errno = EIO; 94 return -1; 95 } 96 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 97 } 98 /* Re-setup the variables. */ 99 usa_count = le16_to_cpu(b->usa_count) - 1; 100 data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 101 /* Fixup all sectors. */ 102 while (usa_count--) { 103 /* 104 * Increment position in usa and restore original data from 105 * the usa into the data buffer. 106 */ 107 *data_pos = *(++usa_pos); 108 /* Increment position in data as well. */ 109 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 110 } 111 return 0; 112 } 113 114 /** 115 * ntfs_mst_pre_write_fixup - apply multi sector transfer protection 116 * @b: pointer to the data to protect 117 * @size: size in bytes of @b 118 * 119 * Perform the necessary pre write multi sector transfer fixup on the data 120 * pointer to by @b of @size. 121 * 122 * Return 0 if fixups applied successfully or -1 if no fixups were performed 123 * due to errors. In that case errno i set to the error code (EINVAL). 124 * 125 * NOTE: We consider the absence / invalidity of an update sequence array to 126 * mean error. This means that you have to create a valid update sequence 127 * array header in the ntfs record before calling this function, otherwise it 128 * will fail (the header needs to contain the position of the update sequence 129 * array together with the number of elements in the array). You also need to 130 * initialise the update sequence number before calling this function 131 * otherwise a random word will be used (whatever was in the record at that 132 * position at that time). 133 */ 134 int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) 135 { 136 u16 usa_ofs, usa_count, usn; 137 u16 *usa_pos, *data_pos; 138 139 ntfs_log_trace("Entering\n"); 140 141 /* Sanity check + only fixup if it makes sense. */ 142 if (!b || ntfs_is_baad_record(b->magic) || 143 ntfs_is_hole_record(b->magic)) { 144 errno = EINVAL; 145 return -1; 146 } 147 /* Setup the variables. */ 148 usa_ofs = le16_to_cpu(b->usa_ofs); 149 /* Decrement usa_count to get number of fixups. */ 150 usa_count = le16_to_cpu(b->usa_count) - 1; 151 /* Size and alignment checks. */ 152 if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 153 (u32)(usa_ofs + (usa_count * 2)) > size || 154 (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { 155 errno = EINVAL; 156 return -1; 157 } 158 /* Position of usn in update sequence array. */ 159 usa_pos = (u16*)((u8*)b + usa_ofs); 160 /* 161 * Cyclically increment the update sequence number 162 * (skipping 0 and -1, i.e. 0xffff). 163 */ 164 usn = le16_to_cpup(usa_pos) + 1; 165 if (usn == 0xffff || !usn) 166 usn = 1; 167 usn = cpu_to_le16(usn); 168 *usa_pos = usn; 169 /* Position in data of first u16 that needs fixing up. */ 170 data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 171 /* Fixup all sectors. */ 172 while (usa_count--) { 173 /* 174 * Increment the position in the usa and save the 175 * original data from the data buffer into the usa. 176 */ 177 *(++usa_pos) = *data_pos; 178 /* Apply fixup to data. */ 179 *data_pos = usn; 180 /* Increment position in data as well. */ 181 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 182 } 183 return 0; 184 } 185 186 /** 187 * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data 188 * @b: pointer to the data to deprotect 189 * 190 * Perform the necessary post write multi sector transfer fixup, not checking 191 * for any errors, because we assume we have just used 192 * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never 193 * have gotten here. 194 */ 195 void ntfs_mst_post_write_fixup(NTFS_RECORD *b) 196 { 197 u16 *usa_pos, *data_pos; 198 199 u16 usa_ofs = le16_to_cpu(b->usa_ofs); 200 u16 usa_count = le16_to_cpu(b->usa_count) - 1; 201 202 ntfs_log_trace("Entering\n"); 203 204 /* Position of usn in update sequence array. */ 205 usa_pos = (u16*)b + usa_ofs/sizeof(u16); 206 207 /* Position in protected data of first u16 that needs fixing up. */ 208 data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 209 210 /* Fixup all sectors. */ 211 while (usa_count--) { 212 /* 213 * Increment position in usa and restore original data from 214 * the usa into the data buffer. 215 */ 216 *data_pos = *(++usa_pos); 217 218 /* Increment position in data as well. */ 219 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 220 } 221 } 222 223