180bca3d3SAxel Dörfler /** 280bca3d3SAxel Dörfler * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. 380bca3d3SAxel Dörfler * 480bca3d3SAxel Dörfler * Copyright (c) 2000-2004 Anton Altaparmakov 530bc84e9SGerasim Troeglazov * Copyright (c) 2006-2009 Szabolcs Szakacsits 680bca3d3SAxel Dörfler * 780bca3d3SAxel Dörfler * This program/include file is free software; you can redistribute it and/or 880bca3d3SAxel Dörfler * modify it under the terms of the GNU General Public License as published 980bca3d3SAxel Dörfler * by the Free Software Foundation; either version 2 of the License, or 1080bca3d3SAxel Dörfler * (at your option) any later version. 1180bca3d3SAxel Dörfler * 1280bca3d3SAxel Dörfler * This program/include file is distributed in the hope that it will be 1380bca3d3SAxel Dörfler * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 1480bca3d3SAxel Dörfler * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1580bca3d3SAxel Dörfler * GNU General Public License for more details. 1680bca3d3SAxel Dörfler * 1780bca3d3SAxel Dörfler * You should have received a copy of the GNU General Public License 1880bca3d3SAxel Dörfler * along with this program (in the main directory of the NTFS-3G 1980bca3d3SAxel Dörfler * distribution in the file COPYING); if not, write to the Free Software 2080bca3d3SAxel Dörfler * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2180bca3d3SAxel Dörfler */ 2280bca3d3SAxel Dörfler 2380bca3d3SAxel Dörfler #ifdef HAVE_CONFIG_H 2480bca3d3SAxel Dörfler #include "config.h" 2580bca3d3SAxel Dörfler #endif 2680bca3d3SAxel Dörfler 2780bca3d3SAxel Dörfler #ifdef HAVE_ERRNO_H 2880bca3d3SAxel Dörfler #include <errno.h> 2980bca3d3SAxel Dörfler #endif 3080bca3d3SAxel Dörfler 3180bca3d3SAxel Dörfler #include "mst.h" 3280bca3d3SAxel Dörfler #include "logging.h" 3380bca3d3SAxel Dörfler 3480bca3d3SAxel Dörfler /** 3580bca3d3SAxel Dörfler * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data 3680bca3d3SAxel Dörfler * @b: pointer to the data to deprotect 3780bca3d3SAxel Dörfler * @size: size in bytes of @b 3880bca3d3SAxel Dörfler * 3980bca3d3SAxel Dörfler * Perform the necessary post read multi sector transfer fixups and detect the 4080bca3d3SAxel Dörfler * presence of incomplete multi sector transfers. - In that case, overwrite the 4180bca3d3SAxel Dörfler * magic of the ntfs record header being processed with "BAAD" (in memory only!) 4280bca3d3SAxel Dörfler * and abort processing. 4380bca3d3SAxel Dörfler * 4480bca3d3SAxel Dörfler * Return 0 on success and -1 on error, with errno set to the error code. The 4580bca3d3SAxel Dörfler * following error codes are defined: 4680bca3d3SAxel Dörfler * EINVAL Invalid arguments or invalid NTFS record in buffer @b. 4780bca3d3SAxel Dörfler * EIO Multi sector transfer error was detected. Magic of the NTFS 4880bca3d3SAxel Dörfler * record in @b will have been set to "BAAD". 4980bca3d3SAxel Dörfler */ 50*a814d850Sthreedeyes int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, 51*a814d850Sthreedeyes BOOL warn) 5280bca3d3SAxel Dörfler { 5380bca3d3SAxel Dörfler u16 usa_ofs, usa_count, usn; 5480bca3d3SAxel Dörfler u16 *usa_pos, *data_pos; 5580bca3d3SAxel Dörfler 5680bca3d3SAxel Dörfler ntfs_log_trace("Entering\n"); 5780bca3d3SAxel Dörfler 5880bca3d3SAxel Dörfler /* Setup the variables. */ 5980bca3d3SAxel Dörfler usa_ofs = le16_to_cpu(b->usa_ofs); 6080bca3d3SAxel Dörfler /* Decrement usa_count to get number of fixups. */ 6180bca3d3SAxel Dörfler usa_count = le16_to_cpu(b->usa_count) - 1; 6280bca3d3SAxel Dörfler /* Size and alignment checks. */ 6380bca3d3SAxel Dörfler if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 6480bca3d3SAxel Dörfler (u32)(usa_ofs + (usa_count * 2)) > size || 6580bca3d3SAxel Dörfler (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { 6680bca3d3SAxel Dörfler errno = EINVAL; 67*a814d850Sthreedeyes if (warn) { 68*a814d850Sthreedeyes ntfs_log_perror("%s: magic: 0x%08lx size: %ld " 69*a814d850Sthreedeyes " usa_ofs: %d usa_count: %u", 70*a814d850Sthreedeyes __FUNCTION__, 71*a814d850Sthreedeyes (long)le32_to_cpu(*(le32 *)b), 72*a814d850Sthreedeyes (long)size, (int)usa_ofs, 73*a814d850Sthreedeyes (unsigned int)usa_count); 74*a814d850Sthreedeyes } 7580bca3d3SAxel Dörfler return -1; 7680bca3d3SAxel Dörfler } 7780bca3d3SAxel Dörfler /* Position of usn in update sequence array. */ 7880bca3d3SAxel Dörfler usa_pos = (u16*)b + usa_ofs/sizeof(u16); 7980bca3d3SAxel Dörfler /* 8080bca3d3SAxel Dörfler * The update sequence number which has to be equal to each of the 8180bca3d3SAxel Dörfler * u16 values before they are fixed up. Note no need to care for 8280bca3d3SAxel Dörfler * endianness since we are comparing and moving data for on disk 8380bca3d3SAxel Dörfler * structures which means the data is consistent. - If it is 8480bca3d3SAxel Dörfler * consistency the wrong endianness it doesn't make any difference. 8580bca3d3SAxel Dörfler */ 8680bca3d3SAxel Dörfler usn = *usa_pos; 8780bca3d3SAxel Dörfler /* 8880bca3d3SAxel Dörfler * Position in protected data of first u16 that needs fixing up. 8980bca3d3SAxel Dörfler */ 9080bca3d3SAxel Dörfler data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 9180bca3d3SAxel Dörfler /* 9280bca3d3SAxel Dörfler * Check for incomplete multi sector transfer(s). 9380bca3d3SAxel Dörfler */ 9480bca3d3SAxel Dörfler while (usa_count--) { 9580bca3d3SAxel Dörfler if (*data_pos != usn) { 9680bca3d3SAxel Dörfler /* 9780bca3d3SAxel Dörfler * Incomplete multi sector transfer detected! )-: 9880bca3d3SAxel Dörfler * Set the magic to "BAAD" and return failure. 9980bca3d3SAxel Dörfler * Note that magic_BAAD is already converted to le32. 10080bca3d3SAxel Dörfler */ 10180bca3d3SAxel Dörfler errno = EIO; 10230bc84e9SGerasim Troeglazov ntfs_log_perror("Incomplete multi-sector transfer: " 10330bc84e9SGerasim Troeglazov "magic: 0x%08x size: %d usa_ofs: %d usa_count:" 10430bc84e9SGerasim Troeglazov " %d data: %d usn: %d", *(le32 *)b, size, 10530bc84e9SGerasim Troeglazov usa_ofs, usa_count, *data_pos, usn); 10630bc84e9SGerasim Troeglazov b->magic = magic_BAAD; 10780bca3d3SAxel Dörfler return -1; 10880bca3d3SAxel Dörfler } 10980bca3d3SAxel Dörfler data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 11080bca3d3SAxel Dörfler } 11180bca3d3SAxel Dörfler /* Re-setup the variables. */ 11280bca3d3SAxel Dörfler usa_count = le16_to_cpu(b->usa_count) - 1; 11380bca3d3SAxel Dörfler data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 11480bca3d3SAxel Dörfler /* Fixup all sectors. */ 11580bca3d3SAxel Dörfler while (usa_count--) { 11680bca3d3SAxel Dörfler /* 11780bca3d3SAxel Dörfler * Increment position in usa and restore original data from 11880bca3d3SAxel Dörfler * the usa into the data buffer. 11980bca3d3SAxel Dörfler */ 12080bca3d3SAxel Dörfler *data_pos = *(++usa_pos); 12180bca3d3SAxel Dörfler /* Increment position in data as well. */ 12280bca3d3SAxel Dörfler data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 12380bca3d3SAxel Dörfler } 12480bca3d3SAxel Dörfler return 0; 12580bca3d3SAxel Dörfler } 12680bca3d3SAxel Dörfler 127*a814d850Sthreedeyes /* 128*a814d850Sthreedeyes * Deprotect multi sector transfer protected data 129*a814d850Sthreedeyes * with a warning if an error is found. 130*a814d850Sthreedeyes */ 131*a814d850Sthreedeyes 132*a814d850Sthreedeyes int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) 133*a814d850Sthreedeyes { 134*a814d850Sthreedeyes return (ntfs_mst_post_read_fixup_warn(b,size,TRUE)); 135*a814d850Sthreedeyes } 136*a814d850Sthreedeyes 13780bca3d3SAxel Dörfler /** 13880bca3d3SAxel Dörfler * ntfs_mst_pre_write_fixup - apply multi sector transfer protection 13980bca3d3SAxel Dörfler * @b: pointer to the data to protect 14080bca3d3SAxel Dörfler * @size: size in bytes of @b 14180bca3d3SAxel Dörfler * 14280bca3d3SAxel Dörfler * Perform the necessary pre write multi sector transfer fixup on the data 14380bca3d3SAxel Dörfler * pointer to by @b of @size. 14480bca3d3SAxel Dörfler * 14580bca3d3SAxel Dörfler * Return 0 if fixups applied successfully or -1 if no fixups were performed 14680bca3d3SAxel Dörfler * due to errors. In that case errno i set to the error code (EINVAL). 14780bca3d3SAxel Dörfler * 14880bca3d3SAxel Dörfler * NOTE: We consider the absence / invalidity of an update sequence array to 14980bca3d3SAxel Dörfler * mean error. This means that you have to create a valid update sequence 15080bca3d3SAxel Dörfler * array header in the ntfs record before calling this function, otherwise it 15180bca3d3SAxel Dörfler * will fail (the header needs to contain the position of the update sequence 15280bca3d3SAxel Dörfler * array together with the number of elements in the array). You also need to 15380bca3d3SAxel Dörfler * initialise the update sequence number before calling this function 15480bca3d3SAxel Dörfler * otherwise a random word will be used (whatever was in the record at that 15580bca3d3SAxel Dörfler * position at that time). 15680bca3d3SAxel Dörfler */ 15780bca3d3SAxel Dörfler int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) 15880bca3d3SAxel Dörfler { 15980bca3d3SAxel Dörfler u16 usa_ofs, usa_count, usn; 16080bca3d3SAxel Dörfler u16 *usa_pos, *data_pos; 16180bca3d3SAxel Dörfler 16280bca3d3SAxel Dörfler ntfs_log_trace("Entering\n"); 16380bca3d3SAxel Dörfler 16480bca3d3SAxel Dörfler /* Sanity check + only fixup if it makes sense. */ 16580bca3d3SAxel Dörfler if (!b || ntfs_is_baad_record(b->magic) || 16680bca3d3SAxel Dörfler ntfs_is_hole_record(b->magic)) { 16780bca3d3SAxel Dörfler errno = EINVAL; 16830bc84e9SGerasim Troeglazov ntfs_log_perror("%s: bad argument", __FUNCTION__); 16980bca3d3SAxel Dörfler return -1; 17080bca3d3SAxel Dörfler } 17180bca3d3SAxel Dörfler /* Setup the variables. */ 17280bca3d3SAxel Dörfler usa_ofs = le16_to_cpu(b->usa_ofs); 17380bca3d3SAxel Dörfler /* Decrement usa_count to get number of fixups. */ 17480bca3d3SAxel Dörfler usa_count = le16_to_cpu(b->usa_count) - 1; 17580bca3d3SAxel Dörfler /* Size and alignment checks. */ 17680bca3d3SAxel Dörfler if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 17780bca3d3SAxel Dörfler (u32)(usa_ofs + (usa_count * 2)) > size || 17880bca3d3SAxel Dörfler (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) { 17980bca3d3SAxel Dörfler errno = EINVAL; 18030bc84e9SGerasim Troeglazov ntfs_log_perror("%s", __FUNCTION__); 18180bca3d3SAxel Dörfler return -1; 18280bca3d3SAxel Dörfler } 18380bca3d3SAxel Dörfler /* Position of usn in update sequence array. */ 18480bca3d3SAxel Dörfler usa_pos = (u16*)((u8*)b + usa_ofs); 18580bca3d3SAxel Dörfler /* 18680bca3d3SAxel Dörfler * Cyclically increment the update sequence number 18780bca3d3SAxel Dörfler * (skipping 0 and -1, i.e. 0xffff). 18880bca3d3SAxel Dörfler */ 18980bca3d3SAxel Dörfler usn = le16_to_cpup(usa_pos) + 1; 19080bca3d3SAxel Dörfler if (usn == 0xffff || !usn) 19180bca3d3SAxel Dörfler usn = 1; 19280bca3d3SAxel Dörfler usn = cpu_to_le16(usn); 19380bca3d3SAxel Dörfler *usa_pos = usn; 19480bca3d3SAxel Dörfler /* Position in data of first u16 that needs fixing up. */ 19580bca3d3SAxel Dörfler data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 19680bca3d3SAxel Dörfler /* Fixup all sectors. */ 19780bca3d3SAxel Dörfler while (usa_count--) { 19880bca3d3SAxel Dörfler /* 19980bca3d3SAxel Dörfler * Increment the position in the usa and save the 20080bca3d3SAxel Dörfler * original data from the data buffer into the usa. 20180bca3d3SAxel Dörfler */ 20280bca3d3SAxel Dörfler *(++usa_pos) = *data_pos; 20380bca3d3SAxel Dörfler /* Apply fixup to data. */ 20480bca3d3SAxel Dörfler *data_pos = usn; 20580bca3d3SAxel Dörfler /* Increment position in data as well. */ 20680bca3d3SAxel Dörfler data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 20780bca3d3SAxel Dörfler } 20880bca3d3SAxel Dörfler return 0; 20980bca3d3SAxel Dörfler } 21080bca3d3SAxel Dörfler 21180bca3d3SAxel Dörfler /** 21280bca3d3SAxel Dörfler * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data 21380bca3d3SAxel Dörfler * @b: pointer to the data to deprotect 21480bca3d3SAxel Dörfler * 21580bca3d3SAxel Dörfler * Perform the necessary post write multi sector transfer fixup, not checking 21680bca3d3SAxel Dörfler * for any errors, because we assume we have just used 21780bca3d3SAxel Dörfler * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never 21880bca3d3SAxel Dörfler * have gotten here. 21980bca3d3SAxel Dörfler */ 22080bca3d3SAxel Dörfler void ntfs_mst_post_write_fixup(NTFS_RECORD *b) 22180bca3d3SAxel Dörfler { 22280bca3d3SAxel Dörfler u16 *usa_pos, *data_pos; 22380bca3d3SAxel Dörfler 22480bca3d3SAxel Dörfler u16 usa_ofs = le16_to_cpu(b->usa_ofs); 22580bca3d3SAxel Dörfler u16 usa_count = le16_to_cpu(b->usa_count) - 1; 22680bca3d3SAxel Dörfler 22780bca3d3SAxel Dörfler ntfs_log_trace("Entering\n"); 22880bca3d3SAxel Dörfler 22980bca3d3SAxel Dörfler /* Position of usn in update sequence array. */ 23080bca3d3SAxel Dörfler usa_pos = (u16*)b + usa_ofs/sizeof(u16); 23180bca3d3SAxel Dörfler 23280bca3d3SAxel Dörfler /* Position in protected data of first u16 that needs fixing up. */ 23380bca3d3SAxel Dörfler data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 23480bca3d3SAxel Dörfler 23580bca3d3SAxel Dörfler /* Fixup all sectors. */ 23680bca3d3SAxel Dörfler while (usa_count--) { 23780bca3d3SAxel Dörfler /* 23880bca3d3SAxel Dörfler * Increment position in usa and restore original data from 23980bca3d3SAxel Dörfler * the usa into the data buffer. 24080bca3d3SAxel Dörfler */ 24180bca3d3SAxel Dörfler *data_pos = *(++usa_pos); 24280bca3d3SAxel Dörfler 24380bca3d3SAxel Dörfler /* Increment position in data as well. */ 24480bca3d3SAxel Dörfler data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 24580bca3d3SAxel Dörfler } 24680bca3d3SAxel Dörfler } 24780bca3d3SAxel Dörfler 248