xref: /third_party/ntfs-3g/libntfs-3g/mst.c (revision 987da915)
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