1987da915Sopenharmony_ci/** 2987da915Sopenharmony_ci * mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project. 3987da915Sopenharmony_ci * 4987da915Sopenharmony_ci * Copyright (c) 2000-2004 Anton Altaparmakov 5987da915Sopenharmony_ci * Copyright (c) 2006-2009 Szabolcs Szakacsits 6987da915Sopenharmony_ci * 7987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or 8987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published 9987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or 10987da915Sopenharmony_ci * (at your option) any later version. 11987da915Sopenharmony_ci * 12987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be 13987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 14987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15987da915Sopenharmony_ci * GNU General Public License for more details. 16987da915Sopenharmony_ci * 17987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License 18987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G 19987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software 20987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21987da915Sopenharmony_ci */ 22987da915Sopenharmony_ci 23987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H 24987da915Sopenharmony_ci#include "config.h" 25987da915Sopenharmony_ci#endif 26987da915Sopenharmony_ci 27987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H 28987da915Sopenharmony_ci#include <errno.h> 29987da915Sopenharmony_ci#endif 30987da915Sopenharmony_ci 31987da915Sopenharmony_ci#include "mst.h" 32987da915Sopenharmony_ci#include "logging.h" 33987da915Sopenharmony_ci 34987da915Sopenharmony_ci/* 35987da915Sopenharmony_ci * Basic validation of a NTFS multi-sector record. The record size must be a 36987da915Sopenharmony_ci * multiple of the logical sector size; and the update sequence array must be 37987da915Sopenharmony_ci * properly aligned, of the expected length, and must end before the last le16 38987da915Sopenharmony_ci * in the first logical sector. 39987da915Sopenharmony_ci */ 40987da915Sopenharmony_cistatic BOOL 41987da915Sopenharmony_ciis_valid_record(u32 size, u16 usa_ofs, u16 usa_count) 42987da915Sopenharmony_ci{ 43987da915Sopenharmony_ci return size % NTFS_BLOCK_SIZE == 0 && 44987da915Sopenharmony_ci usa_ofs % 2 == 0 && 45987da915Sopenharmony_ci usa_count == 1 + (size / NTFS_BLOCK_SIZE) && 46987da915Sopenharmony_ci usa_ofs + ((u32)usa_count * 2) <= NTFS_BLOCK_SIZE - 2; 47987da915Sopenharmony_ci} 48987da915Sopenharmony_ci 49987da915Sopenharmony_ci/** 50987da915Sopenharmony_ci * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data 51987da915Sopenharmony_ci * @b: pointer to the data to deprotect 52987da915Sopenharmony_ci * @size: size in bytes of @b 53987da915Sopenharmony_ci * 54987da915Sopenharmony_ci * Perform the necessary post read multi sector transfer fixups and detect the 55987da915Sopenharmony_ci * presence of incomplete multi sector transfers. - In that case, overwrite the 56987da915Sopenharmony_ci * magic of the ntfs record header being processed with "BAAD" (in memory only!) 57987da915Sopenharmony_ci * and abort processing. 58987da915Sopenharmony_ci * 59987da915Sopenharmony_ci * Return 0 on success and -1 on error, with errno set to the error code. The 60987da915Sopenharmony_ci * following error codes are defined: 61987da915Sopenharmony_ci * EINVAL Invalid arguments or invalid NTFS record in buffer @b. 62987da915Sopenharmony_ci * EIO Multi sector transfer error was detected. Magic of the NTFS 63987da915Sopenharmony_ci * record in @b will have been set to "BAAD". 64987da915Sopenharmony_ci */ 65987da915Sopenharmony_ciint ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size, 66987da915Sopenharmony_ci BOOL warn) 67987da915Sopenharmony_ci{ 68987da915Sopenharmony_ci u16 usa_ofs, usa_count, usn; 69987da915Sopenharmony_ci u16 *usa_pos, *data_pos; 70987da915Sopenharmony_ci 71987da915Sopenharmony_ci ntfs_log_trace("Entering\n"); 72987da915Sopenharmony_ci 73987da915Sopenharmony_ci /* Setup the variables. */ 74987da915Sopenharmony_ci usa_ofs = le16_to_cpu(b->usa_ofs); 75987da915Sopenharmony_ci usa_count = le16_to_cpu(b->usa_count); 76987da915Sopenharmony_ci 77987da915Sopenharmony_ci if (!is_valid_record(size, usa_ofs, usa_count)) { 78987da915Sopenharmony_ci errno = EINVAL; 79987da915Sopenharmony_ci if (warn) { 80987da915Sopenharmony_ci ntfs_log_perror("%s: magic: 0x%08lx size: %ld " 81987da915Sopenharmony_ci " usa_ofs: %d usa_count: %u", 82987da915Sopenharmony_ci __FUNCTION__, 83987da915Sopenharmony_ci (long)le32_to_cpu(*(le32 *)b), 84987da915Sopenharmony_ci (long)size, (int)usa_ofs, 85987da915Sopenharmony_ci (unsigned int)usa_count); 86987da915Sopenharmony_ci } 87987da915Sopenharmony_ci return -1; 88987da915Sopenharmony_ci } 89987da915Sopenharmony_ci /* Position of usn in update sequence array. */ 90987da915Sopenharmony_ci usa_pos = (u16*)b + usa_ofs/sizeof(u16); 91987da915Sopenharmony_ci /* 92987da915Sopenharmony_ci * The update sequence number which has to be equal to each of the 93987da915Sopenharmony_ci * u16 values before they are fixed up. Note no need to care for 94987da915Sopenharmony_ci * endianness since we are comparing and moving data for on disk 95987da915Sopenharmony_ci * structures which means the data is consistent. - If it is 96987da915Sopenharmony_ci * consistency the wrong endianness it doesn't make any difference. 97987da915Sopenharmony_ci */ 98987da915Sopenharmony_ci usn = *usa_pos; 99987da915Sopenharmony_ci /* 100987da915Sopenharmony_ci * Position in protected data of first u16 that needs fixing up. 101987da915Sopenharmony_ci */ 102987da915Sopenharmony_ci data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 103987da915Sopenharmony_ci /* 104987da915Sopenharmony_ci * Check for incomplete multi sector transfer(s). 105987da915Sopenharmony_ci */ 106987da915Sopenharmony_ci while (--usa_count) { 107987da915Sopenharmony_ci if (*data_pos != usn) { 108987da915Sopenharmony_ci /* 109987da915Sopenharmony_ci * Incomplete multi sector transfer detected! )-: 110987da915Sopenharmony_ci * Set the magic to "BAAD" and return failure. 111987da915Sopenharmony_ci * Note that magic_BAAD is already converted to le32. 112987da915Sopenharmony_ci */ 113987da915Sopenharmony_ci errno = EIO; 114987da915Sopenharmony_ci ntfs_log_perror("Incomplete multi-sector transfer: " 115987da915Sopenharmony_ci "magic: 0x%08x size: %d usa_ofs: %d usa_count:" 116987da915Sopenharmony_ci " %d data: %d usn: %d", le32_to_cpu(*(le32 *)b), size, 117987da915Sopenharmony_ci usa_ofs, usa_count, *data_pos, usn); 118987da915Sopenharmony_ci b->magic = magic_BAAD; 119987da915Sopenharmony_ci return -1; 120987da915Sopenharmony_ci } 121987da915Sopenharmony_ci data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 122987da915Sopenharmony_ci } 123987da915Sopenharmony_ci /* Re-setup the variables. */ 124987da915Sopenharmony_ci usa_count = le16_to_cpu(b->usa_count); 125987da915Sopenharmony_ci data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 126987da915Sopenharmony_ci /* Fixup all sectors. */ 127987da915Sopenharmony_ci while (--usa_count) { 128987da915Sopenharmony_ci /* 129987da915Sopenharmony_ci * Increment position in usa and restore original data from 130987da915Sopenharmony_ci * the usa into the data buffer. 131987da915Sopenharmony_ci */ 132987da915Sopenharmony_ci *data_pos = *(++usa_pos); 133987da915Sopenharmony_ci /* Increment position in data as well. */ 134987da915Sopenharmony_ci data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 135987da915Sopenharmony_ci } 136987da915Sopenharmony_ci return 0; 137987da915Sopenharmony_ci} 138987da915Sopenharmony_ci 139987da915Sopenharmony_ci/* 140987da915Sopenharmony_ci * Deprotect multi sector transfer protected data 141987da915Sopenharmony_ci * with a warning if an error is found. 142987da915Sopenharmony_ci */ 143987da915Sopenharmony_ci 144987da915Sopenharmony_ciint ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size) 145987da915Sopenharmony_ci{ 146987da915Sopenharmony_ci return (ntfs_mst_post_read_fixup_warn(b,size,TRUE)); 147987da915Sopenharmony_ci} 148987da915Sopenharmony_ci 149987da915Sopenharmony_ci/** 150987da915Sopenharmony_ci * ntfs_mst_pre_write_fixup - apply multi sector transfer protection 151987da915Sopenharmony_ci * @b: pointer to the data to protect 152987da915Sopenharmony_ci * @size: size in bytes of @b 153987da915Sopenharmony_ci * 154987da915Sopenharmony_ci * Perform the necessary pre write multi sector transfer fixup on the data 155987da915Sopenharmony_ci * pointer to by @b of @size. 156987da915Sopenharmony_ci * 157987da915Sopenharmony_ci * Return 0 if fixups applied successfully or -1 if no fixups were performed 158987da915Sopenharmony_ci * due to errors. In that case errno i set to the error code (EINVAL). 159987da915Sopenharmony_ci * 160987da915Sopenharmony_ci * NOTE: We consider the absence / invalidity of an update sequence array to 161987da915Sopenharmony_ci * mean error. This means that you have to create a valid update sequence 162987da915Sopenharmony_ci * array header in the ntfs record before calling this function, otherwise it 163987da915Sopenharmony_ci * will fail (the header needs to contain the position of the update sequence 164987da915Sopenharmony_ci * array together with the number of elements in the array). You also need to 165987da915Sopenharmony_ci * initialise the update sequence number before calling this function 166987da915Sopenharmony_ci * otherwise a random word will be used (whatever was in the record at that 167987da915Sopenharmony_ci * position at that time). 168987da915Sopenharmony_ci */ 169987da915Sopenharmony_ciint ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size) 170987da915Sopenharmony_ci{ 171987da915Sopenharmony_ci u16 usa_ofs, usa_count, usn; 172987da915Sopenharmony_ci le16 le_usn; 173987da915Sopenharmony_ci le16 *usa_pos, *data_pos; 174987da915Sopenharmony_ci 175987da915Sopenharmony_ci ntfs_log_trace("Entering\n"); 176987da915Sopenharmony_ci 177987da915Sopenharmony_ci /* Sanity check + only fixup if it makes sense. */ 178987da915Sopenharmony_ci if (!b || ntfs_is_baad_record(b->magic) || 179987da915Sopenharmony_ci ntfs_is_hole_record(b->magic)) { 180987da915Sopenharmony_ci errno = EINVAL; 181987da915Sopenharmony_ci ntfs_log_perror("%s: bad argument", __FUNCTION__); 182987da915Sopenharmony_ci return -1; 183987da915Sopenharmony_ci } 184987da915Sopenharmony_ci /* Setup the variables. */ 185987da915Sopenharmony_ci usa_ofs = le16_to_cpu(b->usa_ofs); 186987da915Sopenharmony_ci usa_count = le16_to_cpu(b->usa_count); 187987da915Sopenharmony_ci 188987da915Sopenharmony_ci if (!is_valid_record(size, usa_ofs, usa_count)) { 189987da915Sopenharmony_ci errno = EINVAL; 190987da915Sopenharmony_ci ntfs_log_perror("%s", __FUNCTION__); 191987da915Sopenharmony_ci return -1; 192987da915Sopenharmony_ci } 193987da915Sopenharmony_ci /* Position of usn in update sequence array. */ 194987da915Sopenharmony_ci usa_pos = (le16*)((u8*)b + usa_ofs); 195987da915Sopenharmony_ci /* 196987da915Sopenharmony_ci * Cyclically increment the update sequence number 197987da915Sopenharmony_ci * (skipping 0 and -1, i.e. 0xffff). 198987da915Sopenharmony_ci */ 199987da915Sopenharmony_ci usn = le16_to_cpup(usa_pos) + 1; 200987da915Sopenharmony_ci if (usn == 0xffff || !usn) 201987da915Sopenharmony_ci usn = 1; 202987da915Sopenharmony_ci le_usn = cpu_to_le16(usn); 203987da915Sopenharmony_ci *usa_pos = le_usn; 204987da915Sopenharmony_ci /* Position in data of first le16 that needs fixing up. */ 205987da915Sopenharmony_ci data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1; 206987da915Sopenharmony_ci /* Fixup all sectors. */ 207987da915Sopenharmony_ci while (--usa_count) { 208987da915Sopenharmony_ci /* 209987da915Sopenharmony_ci * Increment the position in the usa and save the 210987da915Sopenharmony_ci * original data from the data buffer into the usa. 211987da915Sopenharmony_ci */ 212987da915Sopenharmony_ci *(++usa_pos) = *data_pos; 213987da915Sopenharmony_ci /* Apply fixup to data. */ 214987da915Sopenharmony_ci *data_pos = le_usn; 215987da915Sopenharmony_ci /* Increment position in data as well. */ 216987da915Sopenharmony_ci data_pos += NTFS_BLOCK_SIZE/sizeof(le16); 217987da915Sopenharmony_ci } 218987da915Sopenharmony_ci return 0; 219987da915Sopenharmony_ci} 220987da915Sopenharmony_ci 221987da915Sopenharmony_ci/** 222987da915Sopenharmony_ci * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data 223987da915Sopenharmony_ci * @b: pointer to the data to deprotect 224987da915Sopenharmony_ci * 225987da915Sopenharmony_ci * Perform the necessary post write multi sector transfer fixup, not checking 226987da915Sopenharmony_ci * for any errors, because we assume we have just used 227987da915Sopenharmony_ci * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never 228987da915Sopenharmony_ci * have gotten here. 229987da915Sopenharmony_ci */ 230987da915Sopenharmony_civoid ntfs_mst_post_write_fixup(NTFS_RECORD *b) 231987da915Sopenharmony_ci{ 232987da915Sopenharmony_ci u16 *usa_pos, *data_pos; 233987da915Sopenharmony_ci 234987da915Sopenharmony_ci u16 usa_ofs = le16_to_cpu(b->usa_ofs); 235987da915Sopenharmony_ci u16 usa_count = le16_to_cpu(b->usa_count); 236987da915Sopenharmony_ci 237987da915Sopenharmony_ci ntfs_log_trace("Entering\n"); 238987da915Sopenharmony_ci 239987da915Sopenharmony_ci /* Position of usn in update sequence array. */ 240987da915Sopenharmony_ci usa_pos = (u16*)b + usa_ofs/sizeof(u16); 241987da915Sopenharmony_ci 242987da915Sopenharmony_ci /* Position in protected data of first u16 that needs fixing up. */ 243987da915Sopenharmony_ci data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 244987da915Sopenharmony_ci 245987da915Sopenharmony_ci /* Fixup all sectors. */ 246987da915Sopenharmony_ci while (--usa_count) { 247987da915Sopenharmony_ci /* 248987da915Sopenharmony_ci * Increment position in usa and restore original data from 249987da915Sopenharmony_ci * the usa into the data buffer. 250987da915Sopenharmony_ci */ 251987da915Sopenharmony_ci *data_pos = *(++usa_pos); 252987da915Sopenharmony_ci 253987da915Sopenharmony_ci /* Increment position in data as well. */ 254987da915Sopenharmony_ci data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 255987da915Sopenharmony_ci } 256987da915Sopenharmony_ci} 257987da915Sopenharmony_ci 258