11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * mst.c - NTFS multi sector transfer protection handling code. Part of the 31da177e4SLinus Torvalds * Linux-NTFS project. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (c) 2001-2004 Anton Altaparmakov 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program/include file is free software; you can redistribute it and/or 81da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License as published 91da177e4SLinus Torvalds * by the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds * (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program/include file is distributed in the hope that it will be 131da177e4SLinus Torvalds * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 141da177e4SLinus Torvalds * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds * GNU General Public License for more details. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds * along with this program (in the main directory of the Linux-NTFS 191da177e4SLinus Torvalds * distribution in the file COPYING); if not, write to the Free Software 201da177e4SLinus Torvalds * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include "ntfs.h" 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds /** 261da177e4SLinus Torvalds * post_read_mst_fixup - deprotect multi sector transfer protected data 271da177e4SLinus Torvalds * @b: pointer to the data to deprotect 281da177e4SLinus Torvalds * @size: size in bytes of @b 291da177e4SLinus Torvalds * 301da177e4SLinus Torvalds * Perform the necessary post read multi sector transfer fixup and detect the 311da177e4SLinus Torvalds * presence of incomplete multi sector transfers. - In that case, overwrite the 321da177e4SLinus Torvalds * magic of the ntfs record header being processed with "BAAD" (in memory only!) 331da177e4SLinus Torvalds * and abort processing. 341da177e4SLinus Torvalds * 351da177e4SLinus Torvalds * Return 0 on success and -EINVAL on error ("BAAD" magic will be present). 361da177e4SLinus Torvalds * 371da177e4SLinus Torvalds * NOTE: We consider the absence / invalidity of an update sequence array to 381da177e4SLinus Torvalds * mean that the structure is not protected at all and hence doesn't need to 391da177e4SLinus Torvalds * be fixed up. Thus, we return success and not failure in this case. This is 401da177e4SLinus Torvalds * in contrast to pre_write_mst_fixup(), see below. 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds int post_read_mst_fixup(NTFS_RECORD *b, const u32 size) 431da177e4SLinus Torvalds { 441da177e4SLinus Torvalds u16 usa_ofs, usa_count, usn; 451da177e4SLinus Torvalds u16 *usa_pos, *data_pos; 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds /* Setup the variables. */ 481da177e4SLinus Torvalds usa_ofs = le16_to_cpu(b->usa_ofs); 491da177e4SLinus Torvalds /* Decrement usa_count to get number of fixups. */ 501da177e4SLinus Torvalds usa_count = le16_to_cpu(b->usa_count) - 1; 511da177e4SLinus Torvalds /* Size and alignment checks. */ 521da177e4SLinus Torvalds if ( size & (NTFS_BLOCK_SIZE - 1) || 531da177e4SLinus Torvalds usa_ofs & 1 || 541da177e4SLinus Torvalds usa_ofs + (usa_count * 2) > size || 551da177e4SLinus Torvalds (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) 561da177e4SLinus Torvalds return 0; 571da177e4SLinus Torvalds /* Position of usn in update sequence array. */ 581da177e4SLinus Torvalds usa_pos = (u16*)b + usa_ofs/sizeof(u16); 591da177e4SLinus Torvalds /* 601da177e4SLinus Torvalds * The update sequence number which has to be equal to each of the 611da177e4SLinus Torvalds * u16 values before they are fixed up. Note no need to care for 621da177e4SLinus Torvalds * endianness since we are comparing and moving data for on disk 631da177e4SLinus Torvalds * structures which means the data is consistent. - If it is 641da177e4SLinus Torvalds * consistenty the wrong endianness it doesn't make any difference. 651da177e4SLinus Torvalds */ 661da177e4SLinus Torvalds usn = *usa_pos; 671da177e4SLinus Torvalds /* 681da177e4SLinus Torvalds * Position in protected data of first u16 that needs fixing up. 691da177e4SLinus Torvalds */ 701da177e4SLinus Torvalds data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 711da177e4SLinus Torvalds /* 721da177e4SLinus Torvalds * Check for incomplete multi sector transfer(s). 731da177e4SLinus Torvalds */ 741da177e4SLinus Torvalds while (usa_count--) { 751da177e4SLinus Torvalds if (*data_pos != usn) { 761da177e4SLinus Torvalds /* 771da177e4SLinus Torvalds * Incomplete multi sector transfer detected! )-: 781da177e4SLinus Torvalds * Set the magic to "BAAD" and return failure. 791da177e4SLinus Torvalds * Note that magic_BAAD is already converted to le32. 801da177e4SLinus Torvalds */ 811da177e4SLinus Torvalds b->magic = magic_BAAD; 821da177e4SLinus Torvalds return -EINVAL; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds /* Re-setup the variables. */ 871da177e4SLinus Torvalds usa_count = le16_to_cpu(b->usa_count) - 1; 881da177e4SLinus Torvalds data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 891da177e4SLinus Torvalds /* Fixup all sectors. */ 901da177e4SLinus Torvalds while (usa_count--) { 911da177e4SLinus Torvalds /* 921da177e4SLinus Torvalds * Increment position in usa and restore original data from 931da177e4SLinus Torvalds * the usa into the data buffer. 941da177e4SLinus Torvalds */ 951da177e4SLinus Torvalds *data_pos = *(++usa_pos); 961da177e4SLinus Torvalds /* Increment position in data as well. */ 971da177e4SLinus Torvalds data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds return 0; 1001da177e4SLinus Torvalds } 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds /** 1031da177e4SLinus Torvalds * pre_write_mst_fixup - apply multi sector transfer protection 1041da177e4SLinus Torvalds * @b: pointer to the data to protect 1051da177e4SLinus Torvalds * @size: size in bytes of @b 1061da177e4SLinus Torvalds * 1071da177e4SLinus Torvalds * Perform the necessary pre write multi sector transfer fixup on the data 1081da177e4SLinus Torvalds * pointer to by @b of @size. 1091da177e4SLinus Torvalds * 1101da177e4SLinus Torvalds * Return 0 if fixup applied (success) or -EINVAL if no fixup was performed 1111da177e4SLinus Torvalds * (assumed not needed). This is in contrast to post_read_mst_fixup() above. 1121da177e4SLinus Torvalds * 1131da177e4SLinus Torvalds * NOTE: We consider the absence / invalidity of an update sequence array to 1141da177e4SLinus Torvalds * mean that the structure is not subject to protection and hence doesn't need 1151da177e4SLinus Torvalds * to be fixed up. This means that you have to create a valid update sequence 1161da177e4SLinus Torvalds * array header in the ntfs record before calling this function, otherwise it 1171da177e4SLinus Torvalds * will fail (the header needs to contain the position of the update sequence 1181da177e4SLinus Torvalds * array together with the number of elements in the array). You also need to 1191da177e4SLinus Torvalds * initialise the update sequence number before calling this function 1201da177e4SLinus Torvalds * otherwise a random word will be used (whatever was in the record at that 1211da177e4SLinus Torvalds * position at that time). 1221da177e4SLinus Torvalds */ 1231da177e4SLinus Torvalds int pre_write_mst_fixup(NTFS_RECORD *b, const u32 size) 1241da177e4SLinus Torvalds { 1251da177e4SLinus Torvalds le16 *usa_pos, *data_pos; 1261da177e4SLinus Torvalds u16 usa_ofs, usa_count, usn; 1271da177e4SLinus Torvalds le16 le_usn; 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds /* Sanity check + only fixup if it makes sense. */ 1301da177e4SLinus Torvalds if (!b || ntfs_is_baad_record(b->magic) || 1311da177e4SLinus Torvalds ntfs_is_hole_record(b->magic)) 1321da177e4SLinus Torvalds return -EINVAL; 1331da177e4SLinus Torvalds /* Setup the variables. */ 1341da177e4SLinus Torvalds usa_ofs = le16_to_cpu(b->usa_ofs); 1351da177e4SLinus Torvalds /* Decrement usa_count to get number of fixups. */ 1361da177e4SLinus Torvalds usa_count = le16_to_cpu(b->usa_count) - 1; 1371da177e4SLinus Torvalds /* Size and alignment checks. */ 1381da177e4SLinus Torvalds if ( size & (NTFS_BLOCK_SIZE - 1) || 1391da177e4SLinus Torvalds usa_ofs & 1 || 1401da177e4SLinus Torvalds usa_ofs + (usa_count * 2) > size || 1411da177e4SLinus Torvalds (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) 1421da177e4SLinus Torvalds return -EINVAL; 1431da177e4SLinus Torvalds /* Position of usn in update sequence array. */ 1441da177e4SLinus Torvalds usa_pos = (le16*)((u8*)b + usa_ofs); 1451da177e4SLinus Torvalds /* 1461da177e4SLinus Torvalds * Cyclically increment the update sequence number 1471da177e4SLinus Torvalds * (skipping 0 and -1, i.e. 0xffff). 1481da177e4SLinus Torvalds */ 1491da177e4SLinus Torvalds usn = le16_to_cpup(usa_pos) + 1; 1501da177e4SLinus Torvalds if (usn == 0xffff || !usn) 1511da177e4SLinus Torvalds usn = 1; 1521da177e4SLinus Torvalds le_usn = cpu_to_le16(usn); 1531da177e4SLinus Torvalds *usa_pos = le_usn; 1541da177e4SLinus Torvalds /* Position in data of first u16 that needs fixing up. */ 1551da177e4SLinus Torvalds data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1; 1561da177e4SLinus Torvalds /* Fixup all sectors. */ 1571da177e4SLinus Torvalds while (usa_count--) { 1581da177e4SLinus Torvalds /* 1591da177e4SLinus Torvalds * Increment the position in the usa and save the 1601da177e4SLinus Torvalds * original data from the data buffer into the usa. 1611da177e4SLinus Torvalds */ 1621da177e4SLinus Torvalds *(++usa_pos) = *data_pos; 1631da177e4SLinus Torvalds /* Apply fixup to data. */ 1641da177e4SLinus Torvalds *data_pos = le_usn; 1651da177e4SLinus Torvalds /* Increment position in data as well. */ 1661da177e4SLinus Torvalds data_pos += NTFS_BLOCK_SIZE/sizeof(le16); 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds return 0; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /** 1721da177e4SLinus Torvalds * post_write_mst_fixup - fast deprotect multi sector transfer protected data 1731da177e4SLinus Torvalds * @b: pointer to the data to deprotect 1741da177e4SLinus Torvalds * 1751da177e4SLinus Torvalds * Perform the necessary post write multi sector transfer fixup, not checking 1761da177e4SLinus Torvalds * for any errors, because we assume we have just used pre_write_mst_fixup(), 1771da177e4SLinus Torvalds * thus the data will be fine or we would never have gotten here. 1781da177e4SLinus Torvalds */ 1791da177e4SLinus Torvalds void post_write_mst_fixup(NTFS_RECORD *b) 1801da177e4SLinus Torvalds { 1811da177e4SLinus Torvalds le16 *usa_pos, *data_pos; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds u16 usa_ofs = le16_to_cpu(b->usa_ofs); 1841da177e4SLinus Torvalds u16 usa_count = le16_to_cpu(b->usa_count) - 1; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds /* Position of usn in update sequence array. */ 1871da177e4SLinus Torvalds usa_pos = (le16*)b + usa_ofs/sizeof(le16); 1881da177e4SLinus Torvalds 1891da177e4SLinus Torvalds /* Position in protected data of first u16 that needs fixing up. */ 1901da177e4SLinus Torvalds data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds /* Fixup all sectors. */ 1931da177e4SLinus Torvalds while (usa_count--) { 1941da177e4SLinus Torvalds /* 1951da177e4SLinus Torvalds * Increment position in usa and restore original data from 1961da177e4SLinus Torvalds * the usa into the data buffer. 1971da177e4SLinus Torvalds */ 1981da177e4SLinus Torvalds *data_pos = *(++usa_pos); 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds /* Increment position in data as well. */ 2011da177e4SLinus Torvalds data_pos += NTFS_BLOCK_SIZE/sizeof(le16); 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds } 204