1/*
2 * ntfs_mst.c - NTFS kernel multi sector transfer protection operations.
3 *
4 * Copyright (c) 2006-2008 Anton Altaparmakov.  All Rights Reserved.
5 * Portions Copyright (c) 2006-2008 Apple Inc.  All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 *    this list of conditions and the following disclaimer in the documentation
14 *    and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its
16 *    contributors may be used to endorse or promote products derived from this
17 *    software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * ALTERNATIVELY, provided that this notice and licensing terms are retained in
31 * full, this file may be redistributed and/or modified under the terms of the
32 * GNU General Public License (GPL) Version 2, in which case the provisions of
33 * that version of the GPL will apply to you instead of the license terms
34 * above.  You can obtain a copy of the GPL Version 2 at
35 * http://developer.apple.com/opensource/licenses/gpl-2.txt.
36 */
37
38#include <sys/errno.h>
39
40#include "ntfs.h"
41#include "ntfs_endian.h"
42#include "ntfs_layout.h"
43#include "ntfs_mst.h"
44#include "ntfs_types.h"
45
46/**
47 * ntfs_mst_fixup_post_read - deprotect multi sector transfer protected data
48 * @b:		pointer to the data to deprotect
49 * @size:	size in bytes of @b
50 *
51 * Perform the necessary post read multi sector transfer fixup and detect the
52 * presence of incomplete multi sector transfers.
53 *
54 * In the case of an incomplete transfer being detected, overwrite the magic of
55 * the ntfs record header being processed with "BAAD" and abort processing.
56 *
57 * Return 0 on success and EIO on error ("BAAD" magic will be present).
58 *
59 * NOTE: We consider the absence / invalidity of an update sequence array to
60 * mean that the structure is not protected at all and hence does not need to
61 * be fixed up.  Thus, we return success and not failure in this case.  This is
62 * in contrast to ntfs_mst_fixup_pre_write(), see below.
63 */
64errno_t ntfs_mst_fixup_post_read(NTFS_RECORD *b, const u32 size)
65{
66	u16 *usa_pos, *data_pos;
67	u16 usa_ofs, usa_count, usn;
68
69	/* Setup the variables. */
70	usa_ofs = le16_to_cpu(b->usa_ofs);
71	/* Decrement usa_count to get number of fixups. */
72	usa_count = le16_to_cpu(b->usa_count) - 1;
73	/* Size and alignment checks. */
74	if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
75			(u32)usa_ofs + ((u32)usa_count * 2) > size ||
76			(size >> NTFS_BLOCK_SIZE_SHIFT) != usa_count)
77		return 0;
78	/* Position of usn in update sequence array. */
79	usa_pos = (u16*)b + usa_ofs/sizeof(u16);
80	/*
81	 * The update sequence number which has to be equal to each of the u16
82	 * values before they are fixed up.  Note no need to care for
83	 * endianness since we are comparing and moving data for on disk
84	 * structures which means the data is consistent.  If it is consistenty
85	 * the wrong endianness it does not make any difference.
86	 */
87	usn = *usa_pos;
88	/* Position in protected data of first u16 that needs fixing up. */
89	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
90	/* Check for incomplete multi sector transfer(s). */
91	while (usa_count--) {
92		if (*data_pos != usn) {
93			/*
94			 * Incomplete multi sector transfer detected!  )-:
95			 * Set the magic to "BAAD" and return failure.
96			 * Note that magic_BAAD is already little endian.
97			 */
98			b->magic = magic_BAAD;
99			return EIO;
100		}
101		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
102	}
103	/* Re-setup the variables. */
104	usa_count = le16_to_cpu(b->usa_count) - 1;
105	data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
106	/* Fixup all sectors. */
107	while (usa_count--) {
108		/*
109		 * Increment position in usa and restore original data from
110		 * the usa into the data buffer.
111		 */
112		*data_pos = *(++usa_pos);
113		/* Increment position in data as well. */
114		data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
115	}
116	return 0;
117}
118
119/**
120 * ntfs_mst_fixup_pre_write - apply multi sector transfer protection
121 * @b:		pointer to the data to protect
122 * @size:	size in bytes of @b
123 *
124 * Perform the necessary pre write multi sector transfer fixup on the data
125 * pointed to by @b of @size.
126 *
127 * Return 0 if fixup applied (success) or EINVAL if no fixup was performed
128 * (assumed not needed).  This is in contrast to ntfs_mst_fixup_post_read()
129 * above.
130 *
131 * NOTE: We consider the absence / invalidity of an update sequence array to
132 * mean that the structure is not subject to protection and hence does not need
133 * to be fixed up.  This means that you have to create a valid update sequence
134 * array header in the ntfs record before calling this function, otherwise it
135 * will fail (the header needs to contain the position of the update sequence
136 * array together with the number of elements in the array).  You also need to
137 * initialise the update sequence number before calling this function
138 * otherwise a random word will be used (whatever was in the record at that
139 * position at that time).
140 */
141errno_t ntfs_mst_fixup_pre_write(NTFS_RECORD *b, const u32 size)
142{
143	le16 *usa_pos, *data_pos;
144	u16 usa_ofs, usa_count, usn;
145	le16 le_usn;
146
147	/* Sanity check + only fixup if it makes sense. */
148	if (!b || ntfs_is_baad_record(b->magic) ||
149			ntfs_is_hole_record(b->magic))
150		return EINVAL;
151	/* Setup the variables. */
152	usa_ofs = le16_to_cpu(b->usa_ofs);
153	/* Decrement usa_count to get number of fixups. */
154	usa_count = le16_to_cpu(b->usa_count) - 1;
155	/* Size and alignment checks. */
156	if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
157			(u32)usa_ofs + ((u32)usa_count * 2) > size ||
158			(size >> NTFS_BLOCK_SIZE_SHIFT) != usa_count)
159		return EINVAL;
160	/* Position of usn in update sequence array. */
161	usa_pos = (le16*)((u8*)b + usa_ofs);
162	/*
163	 * Cyclically increment the update sequence number (skipping 0 and -1,
164	 * i.e. 0xffff).
165	 */
166	usn = le16_to_cpup(usa_pos) + 1;
167	if (usn == 0xffff || !usn)
168		usn = 1;
169	le_usn = cpu_to_le16(usn);
170	*usa_pos = le_usn;
171	/* Position in data of first u16 that needs fixing up. */
172	data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1;
173	/* Fixup all sectors. */
174	while (usa_count--) {
175		/*
176		 * Increment the position in the usa and save the
177		 * original data from the data buffer into the usa.
178		 */
179		*(++usa_pos) = *data_pos;
180		/* Apply fixup to data. */
181		*data_pos = le_usn;
182		/* Increment position in data as well. */
183		data_pos += NTFS_BLOCK_SIZE/sizeof(le16);
184	}
185	return 0;
186}
187
188/**
189 * ntfs_mst_fixup_post_write - fast deprotect mst protected data
190 * @b:		pointer to the data to deprotect
191 *
192 * Perform the necessary post write multi sector transfer fixup, not checking
193 * for any errors, because we assume we have just successfully used
194 * ntfs_mst_fixup_pre_write(), thus the data will be fine or we would never
195 * have gotten here.
196 */
197void ntfs_mst_fixup_post_write(NTFS_RECORD *b)
198{
199	le16 *usa_pos, *data_pos;
200	u16 usa_ofs = le16_to_cpu(b->usa_ofs);
201	u16 usa_count = le16_to_cpu(b->usa_count) - 1;
202	/* Position of usn in update sequence array. */
203	usa_pos = (le16*)b + usa_ofs/sizeof(le16);
204	/* Position in protected data of first u16 that needs fixing up. */
205	data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1;
206	/* Fixup all sectors. */
207	while (usa_count--) {
208		/*
209		 * Increment position in usa and restore original data from
210		 * the usa into the data buffer.
211		 */
212		*data_pos = *(++usa_pos);
213		/* Increment position in data as well. */
214		data_pos += NTFS_BLOCK_SIZE/sizeof(le16);
215	}
216}
217