Blame view

block/partitions/ldm.c 42.1 KB
a497ee34a   Christoph Hellwig   block: switch all...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
  /**
   * ldm - Support for Windows Logical Disk Manager (Dynamic Disks)
   *
   * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
97387e3ba   Anton Altaparmakov   LDM: Fix reassemb...
6
   * Copyright (c) 2001-2012 Anton Altaparmakov
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
   * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
   *
631dd1a88   Justin P. Mattock   Update broken web...
9
   * Documentation is available at http://www.linux-ntfs.org/doku.php?id=downloads 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
   */
  
  #include <linux/slab.h>
  #include <linux/pagemap.h>
  #include <linux/stringify.h>
91f06e668   Andy Shevchenko   fs: ldm: don't us...
15
  #include <linux/kernel.h>
7244ad69c   Andy Shevchenko   block/partitions/...
16
  #include <linux/uuid.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
  #include "ldm.h"
  #include "check.h"
  #include "msdos.h"
210eaaaea   Bart Van Assche   block/partitions/...
20
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
28
29
30
   * ldm_debug/info/error/crit - Output an error message
   * @f:    A printf format string containing the message
   * @...:  Variables to substitute into @f
   *
   * ldm_debug() writes a DEBUG level message to the syslog but only if the
   * driver was compiled with debug enabled. Otherwise, the call turns into a NOP.
   */
  #ifndef CONFIG_LDM_DEBUG
  #define ldm_debug(...)	do {} while (0)
  #else
8e24eea72   Harvey Harrison   fs: replace remai...
31
  #define ldm_debug(f, a...) _ldm_printk (KERN_DEBUG, __func__, f, ##a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  #endif
8e24eea72   Harvey Harrison   fs: replace remai...
33
34
35
  #define ldm_crit(f, a...)  _ldm_printk (KERN_CRIT,  __func__, f, ##a)
  #define ldm_error(f, a...) _ldm_printk (KERN_ERR,   __func__, f, ##a)
  #define ldm_info(f, a...)  _ldm_printk (KERN_INFO,  __func__, f, ##a)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

b9075fa96   Joe Perches   treewide: use __p...
37
38
  static __printf(3, 4)
  void _ldm_printk(const char *level, const char *function, const char *fmt, ...)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  {
b9075fa96   Joe Perches   treewide: use __p...
40
  	struct va_format vaf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
  	va_list args;
  
  	va_start (args, fmt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44

b9075fa96   Joe Perches   treewide: use __p...
45
46
47
48
49
50
51
  	vaf.fmt = fmt;
  	vaf.va = &args;
  
  	printk("%s%s(): %pV
  ", level, function, &vaf);
  
  	va_end(args);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
59
60
   * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure
   * @data:  Raw database PRIVHEAD structure loaded from the device
   * @ph:    In-memory privhead structure in which to return parsed information
   *
   * This parses the LDM database PRIVHEAD structure supplied in @data and
   * sets up the in-memory privhead structure @ph with the obtained information.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
61
62
   * Return:  'true'   @ph contains the PRIVHEAD data
   *          'false'  @ph contents are undefined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
   */
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
64
  static bool ldm_parse_privhead(const u8 *data, struct privhead *ph)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
  {
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
66
  	bool is_vista = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67

dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
68
  	BUG_ON(!data || !ph);
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
69
  	if (MAGIC_PRIVHEAD != get_unaligned_be64(data)) {
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
70
  		ldm_error("Cannot find PRIVHEAD structure. LDM database is"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  			" corrupt. Aborting.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
72
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  	}
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
74
75
76
77
78
79
  	ph->ver_major = get_unaligned_be16(data + 0x000C);
  	ph->ver_minor = get_unaligned_be16(data + 0x000E);
  	ph->logical_disk_start = get_unaligned_be64(data + 0x011B);
  	ph->logical_disk_size = get_unaligned_be64(data + 0x0123);
  	ph->config_start = get_unaligned_be64(data + 0x012B);
  	ph->config_size = get_unaligned_be64(data + 0x0133);
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
80
81
82
83
84
85
  	/* Version 2.11 is Win2k/XP and version 2.12 is Vista. */
  	if (ph->ver_major == 2 && ph->ver_minor == 12)
  		is_vista = true;
  	if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) {
  		ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d."
  			" Aborting.", ph->ver_major, ph->ver_minor);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
86
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
88
89
  	ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major,
  			ph->ver_minor, is_vista ? "Vista" : "2000/XP");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  	if (ph->config_size != LDM_DB_SIZE) {	/* 1 MiB in sectors. */
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
91
92
  		/* Warn the user and continue, carefully. */
  		ldm_info("Database is normally %u bytes, it claims to "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  			"be %llu bytes.", LDM_DB_SIZE,
72dd9ca59   Jeff Garzik   partitions/LDM: b...
94
  			(unsigned long long)ph->config_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
96
97
98
  	if ((ph->logical_disk_size == 0) || (ph->logical_disk_start +
  			ph->logical_disk_size > ph->config_start)) {
  		ldm_error("PRIVHEAD disk size doesn't match real disk size");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
99
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  	}
59b9c6291   Christoph Hellwig   partitions/ldm: s...
101
  	if (uuid_parse(data + 0x0030, &ph->disk_id)) {
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
102
  		ldm_error("PRIVHEAD contains an invalid GUID.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
103
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
105
  	ldm_debug("Parsed PRIVHEAD successfully.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
106
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
115
116
117
118
119
  }
  
  /**
   * ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure
   * @data:  Raw database TOCBLOCK structure loaded from the device
   * @toc:   In-memory toc structure in which to return parsed information
   *
   * This parses the LDM Database TOCBLOCK (table of contents) structure supplied
   * in @data and sets up the in-memory tocblock structure @toc with the obtained
   * information.
   *
   * N.B.  The *_start and *_size values returned in @toc are not range-checked.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
120
121
   * Return:  'true'   @toc contains the TOCBLOCK data
   *          'false'  @toc contents are undefined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
123
  static bool ldm_parse_tocblock (const u8 *data, struct tocblock *toc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  {
  	BUG_ON (!data || !toc);
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
126
  	if (MAGIC_TOCBLOCK != get_unaligned_be64(data)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  		ldm_crit ("Cannot find TOCBLOCK, database may be corrupt.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
128
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  	}
  	strncpy (toc->bitmap1_name, data + 0x24, sizeof (toc->bitmap1_name));
  	toc->bitmap1_name[sizeof (toc->bitmap1_name) - 1] = 0;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
132
133
  	toc->bitmap1_start = get_unaligned_be64(data + 0x2E);
  	toc->bitmap1_size  = get_unaligned_be64(data + 0x36);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
  
  	if (strncmp (toc->bitmap1_name, TOC_BITMAP1,
  			sizeof (toc->bitmap1_name)) != 0) {
  		ldm_crit ("TOCBLOCK's first bitmap is '%s', should be '%s'.",
  				TOC_BITMAP1, toc->bitmap1_name);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
139
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
  	}
  	strncpy (toc->bitmap2_name, data + 0x46, sizeof (toc->bitmap2_name));
  	toc->bitmap2_name[sizeof (toc->bitmap2_name) - 1] = 0;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
143
144
  	toc->bitmap2_start = get_unaligned_be64(data + 0x50);
  	toc->bitmap2_size  = get_unaligned_be64(data + 0x58);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
  	if (strncmp (toc->bitmap2_name, TOC_BITMAP2,
  			sizeof (toc->bitmap2_name)) != 0) {
  		ldm_crit ("TOCBLOCK's second bitmap is '%s', should be '%s'.",
  				TOC_BITMAP2, toc->bitmap2_name);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
149
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
  	}
  	ldm_debug ("Parsed TOCBLOCK successfully.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
152
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
158
159
160
161
162
163
164
  }
  
  /**
   * ldm_parse_vmdb - Read the LDM Database VMDB structure
   * @data:  Raw database VMDB structure loaded from the device
   * @vm:    In-memory vmdb structure in which to return parsed information
   *
   * This parses the LDM Database VMDB structure supplied in @data and sets up
   * the in-memory vmdb structure @vm with the obtained information.
   *
   * N.B.  The *_start, *_size and *_seq values will be range-checked later.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
165
166
   * Return:  'true'   @vm contains VMDB info
   *          'false'  @vm contents are undefined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
168
  static bool ldm_parse_vmdb (const u8 *data, struct vmdb *vm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
  {
  	BUG_ON (!data || !vm);
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
171
  	if (MAGIC_VMDB != get_unaligned_be32(data)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  		ldm_crit ("Cannot find the VMDB, database may be corrupt.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
173
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  	}
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
175
176
  	vm->ver_major = get_unaligned_be16(data + 0x12);
  	vm->ver_minor = get_unaligned_be16(data + 0x14);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
  	if ((vm->ver_major != 4) || (vm->ver_minor != 10)) {
  		ldm_error ("Expected VMDB version %d.%d, got %d.%d. "
  			"Aborting.", 4, 10, vm->ver_major, vm->ver_minor);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
180
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  	}
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
182
  	vm->vblk_size     = get_unaligned_be32(data + 0x08);
294f6cf48   Timo Warns   ldm: corrupted pa...
183
184
185
186
  	if (vm->vblk_size == 0) {
  		ldm_error ("Illegal VBLK size");
  		return false;
  	}
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
187
188
  	vm->vblk_offset   = get_unaligned_be32(data + 0x0C);
  	vm->last_vblk_seq = get_unaligned_be32(data + 0x04);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
  
  	ldm_debug ("Parsed VMDB successfully.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
191
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
196
197
198
199
200
  }
  
  /**
   * ldm_compare_privheads - Compare two privhead objects
   * @ph1:  First privhead
   * @ph2:  Second privhead
   *
   * This compares the two privhead structures @ph1 and @ph2.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
201
202
   * Return:  'true'   Identical
   *          'false'  Different
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
204
  static bool ldm_compare_privheads (const struct privhead *ph1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
210
211
212
213
214
  				   const struct privhead *ph2)
  {
  	BUG_ON (!ph1 || !ph2);
  
  	return ((ph1->ver_major          == ph2->ver_major)		&&
  		(ph1->ver_minor          == ph2->ver_minor)		&&
  		(ph1->logical_disk_start == ph2->logical_disk_start)	&&
  		(ph1->logical_disk_size  == ph2->logical_disk_size)	&&
  		(ph1->config_start       == ph2->config_start)		&&
  		(ph1->config_size        == ph2->config_size)		&&
59b9c6291   Christoph Hellwig   partitions/ldm: s...
215
  		uuid_equal(&ph1->disk_id, &ph2->disk_id));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
218
219
220
221
222
223
224
  }
  
  /**
   * ldm_compare_tocblocks - Compare two tocblock objects
   * @toc1:  First toc
   * @toc2:  Second toc
   *
   * This compares the two tocblock structures @toc1 and @toc2.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
225
226
   * Return:  'true'   Identical
   *          'false'  Different
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
228
  static bool ldm_compare_tocblocks (const struct tocblock *toc1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  				   const struct tocblock *toc2)
  {
  	BUG_ON (!toc1 || !toc2);
  
  	return ((toc1->bitmap1_start == toc2->bitmap1_start)	&&
  		(toc1->bitmap1_size  == toc2->bitmap1_size)	&&
  		(toc1->bitmap2_start == toc2->bitmap2_start)	&&
  		(toc1->bitmap2_size  == toc2->bitmap2_size)	&&
  		!strncmp (toc1->bitmap1_name, toc2->bitmap1_name,
  			sizeof (toc1->bitmap1_name))		&&
  		!strncmp (toc1->bitmap2_name, toc2->bitmap2_name,
  			sizeof (toc1->bitmap2_name)));
  }
  
  /**
   * ldm_validate_privheads - Compare the primary privhead with its backups
1493bf217   Tejun Heo   block: use struct...
245
   * @state: Partition check state including device holding the LDM Database
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
253
   * @ph1:   Memory struct to fill with ph contents
   *
   * Read and compare all three privheads from disk.
   *
   * The privheads on disk show the size and location of the main disk area and
   * the configuration area (the database).  The values are range-checked against
   * @hd, which contains the real size of the disk.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
254
255
   * Return:  'true'   Success
   *          'false'  Error
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
   */
1493bf217   Tejun Heo   block: use struct...
257
258
  static bool ldm_validate_privheads(struct parsed_partitions *state,
  				   struct privhead *ph1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
263
  {
  	static const int off[3] = { OFF_PRIV1, OFF_PRIV2, OFF_PRIV3 };
  	struct privhead *ph[3] = { ph1 };
  	Sector sect;
  	u8 *data;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
264
  	bool result = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
  	long num_sects;
  	int i;
1493bf217   Tejun Heo   block: use struct...
267
  	BUG_ON (!state || !ph1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
270
271
272
273
274
275
276
277
278
279
280
  
  	ph[1] = kmalloc (sizeof (*ph[1]), GFP_KERNEL);
  	ph[2] = kmalloc (sizeof (*ph[2]), GFP_KERNEL);
  	if (!ph[1] || !ph[2]) {
  		ldm_crit ("Out of memory.");
  		goto out;
  	}
  
  	/* off[1 & 2] are relative to ph[0]->config_start */
  	ph[0]->config_start = 0;
  
  	/* Read and parse privheads */
  	for (i = 0; i < 3; i++) {
1493bf217   Tejun Heo   block: use struct...
281
282
  		data = read_part_sector(state, ph[0]->config_start + off[i],
  					&sect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  		if (!data) {
  			ldm_crit ("Disk read failed.");
  			goto out;
  		}
  		result = ldm_parse_privhead (data, ph[i]);
  		put_dev_sector (sect);
  		if (!result) {
  			ldm_error ("Cannot find PRIVHEAD %d.", i+1); /* Log again */
  			if (i < 2)
  				goto out;	/* Already logged */
  			else
  				break;	/* FIXME ignore for now, 3rd PH can fail on odd-sized disks */
  		}
  	}
1493bf217   Tejun Heo   block: use struct...
297
  	num_sects = state->bdev->bd_inode->i_size >> 9;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  
  	if ((ph[0]->config_start > num_sects) ||
  	   ((ph[0]->config_start + ph[0]->config_size) > num_sects)) {
  		ldm_crit ("Database extends beyond the end of the disk.");
  		goto out;
  	}
  
  	if ((ph[0]->logical_disk_start > ph[0]->config_start) ||
  	   ((ph[0]->logical_disk_start + ph[0]->logical_disk_size)
  		    > ph[0]->config_start)) {
  		ldm_crit ("Disk and database overlap.");
  		goto out;
  	}
  
  	if (!ldm_compare_privheads (ph[0], ph[1])) {
  		ldm_crit ("Primary and backup PRIVHEADs don't match.");
  		goto out;
  	}
  	/* FIXME ignore this for now
  	if (!ldm_compare_privheads (ph[0], ph[2])) {
  		ldm_crit ("Primary and backup PRIVHEADs don't match.");
  		goto out;
  	}*/
  	ldm_debug ("Validated PRIVHEADs successfully.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
322
  	result = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
329
330
  out:
  	kfree (ph[1]);
  	kfree (ph[2]);
  	return result;
  }
  
  /**
   * ldm_validate_tocblocks - Validate the table of contents and its backups
1493bf217   Tejun Heo   block: use struct...
331
332
   * @state: Partition check state including device holding the LDM Database
   * @base:  Offset, into @state->bdev, of the database
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
335
   * @ldb:   Cache of the database structures
   *
   * Find and compare the four tables of contents of the LDM Database stored on
1493bf217   Tejun Heo   block: use struct...
336
   * @state->bdev and return the parsed information into @toc1.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
339
   *
   * The offsets and sizes of the configs are range-checked against a privhead.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
340
341
   * Return:  'true'   @toc1 contains validated TOCBLOCK info
   *          'false'  @toc1 contents are undefined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
   */
1493bf217   Tejun Heo   block: use struct...
343
344
  static bool ldm_validate_tocblocks(struct parsed_partitions *state,
  				   unsigned long base, struct ldmdb *ldb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
348
349
350
  {
  	static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4};
  	struct tocblock *tb[4];
  	struct privhead *ph;
  	Sector sect;
  	u8 *data;
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
351
  	int i, nr_tbs;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
352
  	bool result = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353

1493bf217   Tejun Heo   block: use struct...
354
  	BUG_ON(!state || !ldb);
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
355
  	ph = &ldb->ph;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  	tb[0] = &ldb->toc;
6da2ec560   Kees Cook   treewide: kmalloc...
357
  	tb[1] = kmalloc_array(3, sizeof(*tb[1]), GFP_KERNEL);
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
358
359
360
  	if (!tb[1]) {
  		ldm_crit("Out of memory.");
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
362
363
364
365
366
367
368
369
370
  	tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1]));
  	tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2]));
  	/*
  	 * Try to read and parse all four TOCBLOCKs.
  	 *
  	 * Windows Vista LDM v2.12 does not always have all four TOCBLOCKs so
  	 * skip any that fail as long as we get at least one valid TOCBLOCK.
  	 */
  	for (nr_tbs = i = 0; i < 4; i++) {
1493bf217   Tejun Heo   block: use struct...
371
  		data = read_part_sector(state, base + off[i], &sect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  		if (!data) {
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
373
374
  			ldm_error("Disk read failed for TOCBLOCK %d.", i);
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  		}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
376
377
378
  		if (ldm_parse_tocblock(data, tb[nr_tbs]))
  			nr_tbs++;
  		put_dev_sector(sect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
380
381
382
383
384
  	if (!nr_tbs) {
  		ldm_crit("Failed to find a valid TOCBLOCK.");
  		goto err;
  	}
  	/* Range check the TOCBLOCK against a privhead. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  	if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) ||
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
386
387
388
389
  			((tb[0]->bitmap2_start + tb[0]->bitmap2_size) >
  			ph->config_size)) {
  		ldm_crit("The bitmaps are out of range.  Giving up.");
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
391
392
393
394
395
396
  	/* Compare all loaded TOCBLOCKs. */
  	for (i = 1; i < nr_tbs; i++) {
  		if (!ldm_compare_tocblocks(tb[0], tb[i])) {
  			ldm_crit("TOCBLOCKs 0 and %d do not match.", i);
  			goto err;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
398
  	ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
399
  	result = true;
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
400
401
  err:
  	kfree(tb[1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
404
405
406
  	return result;
  }
  
  /**
   * ldm_validate_vmdb - Read the VMDB and validate it
1493bf217   Tejun Heo   block: use struct...
407
   * @state: Partition check state including device holding the LDM Database
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
409
410
411
412
413
   * @base:  Offset, into @bdev, of the database
   * @ldb:   Cache of the database structures
   *
   * Find the vmdb of the LDM Database stored on @bdev and return the parsed
   * information in @ldb.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
414
415
   * Return:  'true'   @ldb contains validated VBDB info
   *          'false'  @ldb contents are undefined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
   */
1493bf217   Tejun Heo   block: use struct...
417
418
  static bool ldm_validate_vmdb(struct parsed_partitions *state,
  			      unsigned long base, struct ldmdb *ldb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
  {
  	Sector sect;
  	u8 *data;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
422
  	bool result = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
  	struct vmdb *vm;
  	struct tocblock *toc;
1493bf217   Tejun Heo   block: use struct...
425
  	BUG_ON (!state || !ldb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
428
  
  	vm  = &ldb->vm;
  	toc = &ldb->toc;
1493bf217   Tejun Heo   block: use struct...
429
  	data = read_part_sector(state, base + OFF_VMDB, &sect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
  	if (!data) {
  		ldm_crit ("Disk read failed.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
432
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
435
436
437
438
  	}
  
  	if (!ldm_parse_vmdb (data, vm))
  		goto out;				/* Already logged */
  
  	/* Are there uncommitted transactions? */
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
439
  	if (get_unaligned_be16(data + 0x10) != 0x01) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
  		ldm_crit ("Database is not in a consistent state.  Aborting.");
  		goto out;
  	}
  
  	if (vm->vblk_offset != 512)
  		ldm_info ("VBLKs start at offset 0x%04x.", vm->vblk_offset);
  
  	/*
  	 * The last_vblkd_seq can be before the end of the vmdb, just make sure
  	 * it is not out of bounds.
  	 */
  	if ((vm->vblk_size * vm->last_vblk_seq) > (toc->bitmap1_size << 9)) {
  		ldm_crit ("VMDB exceeds allowed size specified by TOCBLOCK.  "
  				"Database is corrupt.  Aborting.");
  		goto out;
  	}
130c6b989   Richard Knutsson   [PATCH] fs/partit...
456
  	result = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
461
462
463
464
  out:
  	put_dev_sector (sect);
  	return result;
  }
  
  
  /**
   * ldm_validate_partition_table - Determine whether bdev might be a dynamic disk
1493bf217   Tejun Heo   block: use struct...
465
   * @state: Partition check state including device holding the LDM Database
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
471
   *
   * This function provides a weak test to decide whether the device is a dynamic
   * disk or not.  It looks for an MS-DOS-style partition table containing at
   * least one partition of type 0x42 (formerly SFS, now used by Windows for
   * dynamic disks).
   *
1493bf217   Tejun Heo   block: use struct...
472
   * N.B.  The only possible error can come from the read_part_sector and that is
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
   *       only likely to happen if the underlying device is strange.  If that IS
   *       the case we should return zero to let someone else try.
   *
1493bf217   Tejun Heo   block: use struct...
476
477
   * Return:  'true'   @state->bdev is a dynamic disk
   *          'false'  @state->bdev is not a dynamic disk, or an error occurred
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
   */
1493bf217   Tejun Heo   block: use struct...
479
  static bool ldm_validate_partition_table(struct parsed_partitions *state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
484
  {
  	Sector sect;
  	u8 *data;
  	struct partition *p;
  	int i;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
485
  	bool result = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486

1493bf217   Tejun Heo   block: use struct...
487
  	BUG_ON(!state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488

1493bf217   Tejun Heo   block: use struct...
489
  	data = read_part_sector(state, 0, &sect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
  	if (!data) {
1dd45aae7   Nikanth Karthikesan   ldm: Silence "ldm...
491
  		ldm_info ("Disk read failed.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
492
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
496
497
498
499
  	}
  
  	if (*(__le16*) (data + 0x01FE) != cpu_to_le16 (MSDOS_LABEL_MAGIC))
  		goto out;
  
  	p = (struct partition*)(data + 0x01BE);
  	for (i = 0; i < 4; i++, p++)
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
500
  		if (SYS_IND (p) == LDM_PARTITION) {
130c6b989   Richard Knutsson   [PATCH] fs/partit...
501
  			result = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
  			break;
  		}
  
  	if (result)
  		ldm_debug ("Found W2K dynamic disk partition type.");
  
  out:
  	put_dev_sector (sect);
  	return result;
  }
  
  /**
   * ldm_get_disk_objid - Search a linked list of vblk's for a given Disk Id
   * @ldb:  Cache of the database structures
   *
   * The LDM Database contains a list of all partitions on all dynamic disks.
   * The primary PRIVHEAD, at the beginning of the physical disk, tells us
   * the GUID of this disk.  This function searches for the GUID in a linked
   * list of vblk's.
   *
   * Return:  Pointer, A matching vblk was found
   *          NULL,    No match, or an error
   */
  static struct vblk * ldm_get_disk_objid (const struct ldmdb *ldb)
  {
  	struct list_head *item;
  
  	BUG_ON (!ldb);
  
  	list_for_each (item, &ldb->v_disk) {
  		struct vblk *v = list_entry (item, struct vblk, list);
59b9c6291   Christoph Hellwig   partitions/ldm: s...
533
  		if (uuid_equal(&v->vblk.disk.disk_id, &ldb->ph.disk_id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
  			return v;
  	}
  
  	return NULL;
  }
  
  /**
   * ldm_create_data_partitions - Create data partitions for this device
   * @pp:   List of the partitions parsed so far
   * @ldb:  Cache of the database structures
   *
   * The database contains ALL the partitions for ALL disk groups, so we need to
   * filter out this specific disk. Using the disk's object id, we can find all
   * the partitions in the database that belong to this disk.
   *
   * Add each partition in our database, to the parsed_partitions structure.
   *
   * N.B.  This function creates the partitions in the order it finds partition
   *       objects in the linked list.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
554
555
   * Return:  'true'   Partition created
   *          'false'  Error, probably a range checking problem
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
557
  static bool ldm_create_data_partitions (struct parsed_partitions *pp,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
562
563
564
565
566
567
568
569
570
  					const struct ldmdb *ldb)
  {
  	struct list_head *item;
  	struct vblk *vb;
  	struct vblk *disk;
  	struct vblk_part *part;
  	int part_num = 1;
  
  	BUG_ON (!pp || !ldb);
  
  	disk = ldm_get_disk_objid (ldb);
  	if (!disk) {
  		ldm_crit ("Can't find the ID of this disk in the database.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
571
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  	}
9c867fbe0   Alexey Dobriyan   partitions: fix s...
573
  	strlcat(pp->pp_buf, " [LDM]", PAGE_SIZE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
577
578
579
580
581
582
583
584
585
586
  
  	/* Create the data partitions */
  	list_for_each (item, &ldb->v_part) {
  		vb = list_entry (item, struct vblk, list);
  		part = &vb->vblk.part;
  
  		if (part->disk_id != disk->obj_id)
  			continue;
  
  		put_partition (pp, part_num, ldb->ph.logical_disk_start +
  				part->start, part->size);
  		part_num++;
  	}
9c867fbe0   Alexey Dobriyan   partitions: fix s...
587
588
  	strlcat(pp->pp_buf, "
  ", PAGE_SIZE);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
589
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
  }
  
  
  /**
   * ldm_relative - Calculate the next relative offset
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @base:    Size of the previous fixed width fields
   * @offset:  Cumulative size of the previous variable-width fields
   *
   * Because many of the VBLK fields are variable-width, it's necessary
   * to calculate each offset based on the previous one and the length
   * of the field it pointed to.
   *
   * Return:  -1 Error, the calculated offset exceeded the size of the buffer
   *           n OK, a range-checked offset into buffer
   */
959bc220d   Anton Altaparmakov   Fix LDM for new f...
607
  static int ldm_relative(const u8 *buffer, int buflen, int base, int offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
  {
  
  	base += offset;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
611
612
613
614
615
616
617
  	if (!buffer || offset < 0 || base > buflen) {
  		if (!buffer)
  			ldm_error("!buffer");
  		if (offset < 0)
  			ldm_error("offset (%d) < 0", offset);
  		if (base > buflen)
  			ldm_error("base (%d) > buflen (%d)", base, buflen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
  		return -1;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
619
620
621
622
  	}
  	if (base + buffer[base] >= buflen) {
  		ldm_error("base (%d) + buffer[base] (%d) >= buflen (%d)", base,
  				buffer[base], buflen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  		return -1;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
624
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
  	return buffer[base] + offset + 1;
  }
  
  /**
   * ldm_get_vnum - Convert a variable-width, big endian number, into cpu order
   * @block:  Pointer to the variable-width number to convert
   *
   * Large numbers in the LDM Database are often stored in a packed format.  Each
   * number is prefixed by a one byte width marker.  All numbers in the database
   * are stored in big-endian byte order.  This function reads one of these
   * numbers and returns the result
   *
   * N.B.  This function DOES NOT perform any range checking, though the most
   *       it will read is eight bytes.
   *
   * Return:  n A number
   *          0 Zero, or an error occurred
   */
  static u64 ldm_get_vnum (const u8 *block)
  {
  	u64 tmp = 0;
  	u8 length;
  
  	BUG_ON (!block);
  
  	length = *block++;
  
  	if (length && length <= 8)
  		while (length--)
  			tmp = (tmp << 8) | *block++;
  	else
  		ldm_error ("Illegal length %d.", length);
  
  	return tmp;
  }
  
  /**
   * ldm_get_vstr - Read a length-prefixed string into a buffer
   * @block:   Pointer to the length marker
   * @buffer:  Location to copy string to
   * @buflen:  Size of the output buffer
   *
   * Many of the strings in the LDM Database are not NULL terminated.  Instead
   * they are prefixed by a one byte length marker.  This function copies one of
   * these strings into a buffer.
   *
   * N.B.  This function DOES NOT perform any range checking on the input.
   *       If the buffer is too small, the output will be truncated.
   *
   * Return:  0, Error and @buffer contents are undefined
   *          n, String length in characters (excluding NULL)
   *          buflen-1, String was truncated.
   */
  static int ldm_get_vstr (const u8 *block, u8 *buffer, int buflen)
  {
  	int length;
  
  	BUG_ON (!block || !buffer);
  
  	length = block[0];
  	if (length >= buflen) {
  		ldm_error ("Truncating string %d -> %d.", length, buflen);
  		length = buflen - 1;
  	}
  	memcpy (buffer, block + 1, length);
  	buffer[length] = 0;
  	return length;
  }
  
  
  /**
   * ldm_parse_cmp3 - Read a raw VBLK Component object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Component object (version 3) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
703
704
   * Return:  'true'   @vb contains a Component VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
706
  static bool ldm_parse_cmp3 (const u8 *buffer, int buflen, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
  {
  	int r_objid, r_name, r_vstate, r_child, r_parent, r_stripe, r_cols, len;
  	struct vblk_comp *comp;
  
  	BUG_ON (!buffer || !vb);
  
  	r_objid  = ldm_relative (buffer, buflen, 0x18, 0);
  	r_name   = ldm_relative (buffer, buflen, 0x18, r_objid);
  	r_vstate = ldm_relative (buffer, buflen, 0x18, r_name);
  	r_child  = ldm_relative (buffer, buflen, 0x1D, r_vstate);
  	r_parent = ldm_relative (buffer, buflen, 0x2D, r_child);
  
  	if (buffer[0x12] & VBLK_FLAG_COMP_STRIPE) {
  		r_stripe = ldm_relative (buffer, buflen, 0x2E, r_parent);
  		r_cols   = ldm_relative (buffer, buflen, 0x2E, r_stripe);
  		len = r_cols;
  	} else {
  		r_stripe = 0;
  		r_cols   = 0;
  		len = r_parent;
  	}
  	if (len < 0)
130c6b989   Richard Knutsson   [PATCH] fs/partit...
729
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
  
  	len += VBLK_SIZE_CMP3;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
732
  	if (len != get_unaligned_be32(buffer + 0x14))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
733
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
734
735
736
737
738
739
740
741
  
  	comp = &vb->vblk.comp;
  	ldm_get_vstr (buffer + 0x18 + r_name, comp->state,
  		sizeof (comp->state));
  	comp->type      = buffer[0x18 + r_vstate];
  	comp->children  = ldm_get_vnum (buffer + 0x1D + r_vstate);
  	comp->parent_id = ldm_get_vnum (buffer + 0x2D + r_child);
  	comp->chunksize = r_stripe ? ldm_get_vnum (buffer+r_parent+0x2E) : 0;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
742
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
744
745
746
747
748
749
750
751
752
  }
  
  /**
   * ldm_parse_dgr3 - Read a raw VBLK Disk Group object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Disk Group object (version 3) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
753
754
   * Return:  'true'   @vb contains a Disk Group VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
   */
  static int ldm_parse_dgr3 (const u8 *buffer, int buflen, struct vblk *vb)
  {
  	int r_objid, r_name, r_diskid, r_id1, r_id2, len;
  	struct vblk_dgrp *dgrp;
  
  	BUG_ON (!buffer || !vb);
  
  	r_objid  = ldm_relative (buffer, buflen, 0x18, 0);
  	r_name   = ldm_relative (buffer, buflen, 0x18, r_objid);
  	r_diskid = ldm_relative (buffer, buflen, 0x18, r_name);
  
  	if (buffer[0x12] & VBLK_FLAG_DGR3_IDS) {
  		r_id1 = ldm_relative (buffer, buflen, 0x24, r_diskid);
  		r_id2 = ldm_relative (buffer, buflen, 0x24, r_id1);
  		len = r_id2;
  	} else {
  		r_id1 = 0;
  		r_id2 = 0;
  		len = r_diskid;
  	}
  	if (len < 0)
130c6b989   Richard Knutsson   [PATCH] fs/partit...
777
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
  
  	len += VBLK_SIZE_DGR3;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
780
  	if (len != get_unaligned_be32(buffer + 0x14))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
781
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
  
  	dgrp = &vb->vblk.dgrp;
  	ldm_get_vstr (buffer + 0x18 + r_name, dgrp->disk_id,
  		sizeof (dgrp->disk_id));
130c6b989   Richard Knutsson   [PATCH] fs/partit...
786
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787
788
789
790
791
792
793
794
795
796
  }
  
  /**
   * ldm_parse_dgr4 - Read a raw VBLK Disk Group object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Disk Group object (version 4) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
797
798
   * Return:  'true'   @vb contains a Disk Group VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
800
  static bool ldm_parse_dgr4 (const u8 *buffer, int buflen, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
803
  {
  	char buf[64];
  	int r_objid, r_name, r_id1, r_id2, len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
  
  	BUG_ON (!buffer || !vb);
  
  	r_objid  = ldm_relative (buffer, buflen, 0x18, 0);
  	r_name   = ldm_relative (buffer, buflen, 0x18, r_objid);
  
  	if (buffer[0x12] & VBLK_FLAG_DGR4_IDS) {
  		r_id1 = ldm_relative (buffer, buflen, 0x44, r_name);
  		r_id2 = ldm_relative (buffer, buflen, 0x44, r_id1);
  		len = r_id2;
  	} else {
  		r_id1 = 0;
  		r_id2 = 0;
  		len = r_name;
  	}
  	if (len < 0)
130c6b989   Richard Knutsson   [PATCH] fs/partit...
820
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
822
  
  	len += VBLK_SIZE_DGR4;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
823
  	if (len != get_unaligned_be32(buffer + 0x14))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
824
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
  	ldm_get_vstr (buffer + 0x18 + r_objid, buf, sizeof (buf));
130c6b989   Richard Knutsson   [PATCH] fs/partit...
827
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
830
831
832
833
834
835
836
837
  }
  
  /**
   * ldm_parse_dsk3 - Read a raw VBLK Disk object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Disk object (version 3) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
838
839
   * Return:  'true'   @vb contains a Disk VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
841
  static bool ldm_parse_dsk3 (const u8 *buffer, int buflen, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
844
845
846
847
848
849
850
851
852
853
  {
  	int r_objid, r_name, r_diskid, r_altname, len;
  	struct vblk_disk *disk;
  
  	BUG_ON (!buffer || !vb);
  
  	r_objid   = ldm_relative (buffer, buflen, 0x18, 0);
  	r_name    = ldm_relative (buffer, buflen, 0x18, r_objid);
  	r_diskid  = ldm_relative (buffer, buflen, 0x18, r_name);
  	r_altname = ldm_relative (buffer, buflen, 0x18, r_diskid);
  	len = r_altname;
  	if (len < 0)
130c6b989   Richard Knutsson   [PATCH] fs/partit...
854
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
856
  
  	len += VBLK_SIZE_DSK3;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
857
  	if (len != get_unaligned_be32(buffer + 0x14))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
858
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
859
860
861
862
  
  	disk = &vb->vblk.disk;
  	ldm_get_vstr (buffer + 0x18 + r_diskid, disk->alt_name,
  		sizeof (disk->alt_name));
59b9c6291   Christoph Hellwig   partitions/ldm: s...
863
  	if (uuid_parse(buffer + 0x19 + r_name, &disk->disk_id))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
864
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865

130c6b989   Richard Knutsson   [PATCH] fs/partit...
866
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
869
870
871
872
873
874
875
876
  }
  
  /**
   * ldm_parse_dsk4 - Read a raw VBLK Disk object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Disk object (version 4) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
877
878
   * Return:  'true'   @vb contains a Disk VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
880
  static bool ldm_parse_dsk4 (const u8 *buffer, int buflen, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
882
883
884
885
886
887
888
889
890
  {
  	int r_objid, r_name, len;
  	struct vblk_disk *disk;
  
  	BUG_ON (!buffer || !vb);
  
  	r_objid = ldm_relative (buffer, buflen, 0x18, 0);
  	r_name  = ldm_relative (buffer, buflen, 0x18, r_objid);
  	len     = r_name;
  	if (len < 0)
130c6b989   Richard Knutsson   [PATCH] fs/partit...
891
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
893
  
  	len += VBLK_SIZE_DSK4;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
894
  	if (len != get_unaligned_be32(buffer + 0x14))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
895
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
897
  
  	disk = &vb->vblk.disk;
59b9c6291   Christoph Hellwig   partitions/ldm: s...
898
  	uuid_copy(&disk->disk_id, (uuid_t *)(buffer + 0x18 + r_name));
130c6b989   Richard Knutsson   [PATCH] fs/partit...
899
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900
901
902
903
904
905
906
907
908
909
  }
  
  /**
   * ldm_parse_prt3 - Read a raw VBLK Partition object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Partition object (version 3) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
910
911
   * Return:  'true'   @vb contains a Partition VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
912
   */
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
913
  static bool ldm_parse_prt3(const u8 *buffer, int buflen, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
915
916
  {
  	int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len;
  	struct vblk_part *part;
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
  	BUG_ON(!buffer || !vb);
  	r_objid = ldm_relative(buffer, buflen, 0x18, 0);
  	if (r_objid < 0) {
  		ldm_error("r_objid %d < 0", r_objid);
  		return false;
  	}
  	r_name = ldm_relative(buffer, buflen, 0x18, r_objid);
  	if (r_name < 0) {
  		ldm_error("r_name %d < 0", r_name);
  		return false;
  	}
  	r_size = ldm_relative(buffer, buflen, 0x34, r_name);
  	if (r_size < 0) {
  		ldm_error("r_size %d < 0", r_size);
  		return false;
  	}
  	r_parent = ldm_relative(buffer, buflen, 0x34, r_size);
  	if (r_parent < 0) {
  		ldm_error("r_parent %d < 0", r_parent);
  		return false;
  	}
  	r_diskid = ldm_relative(buffer, buflen, 0x34, r_parent);
  	if (r_diskid < 0) {
  		ldm_error("r_diskid %d < 0", r_diskid);
  		return false;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
  	if (buffer[0x12] & VBLK_FLAG_PART_INDEX) {
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
944
945
946
947
948
  		r_index = ldm_relative(buffer, buflen, 0x34, r_diskid);
  		if (r_index < 0) {
  			ldm_error("r_index %d < 0", r_index);
  			return false;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
951
952
953
  		len = r_index;
  	} else {
  		r_index = 0;
  		len = r_diskid;
  	}
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
954
955
  	if (len < 0) {
  		ldm_error("len %d < 0", len);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
956
  		return false;
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
957
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
  	len += VBLK_SIZE_PRT3;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
959
  	if (len > get_unaligned_be32(buffer + 0x14)) {
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
960
  		ldm_error("len %d > BE32(buffer + 0x14) %d", len,
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
961
  				get_unaligned_be32(buffer + 0x14));
130c6b989   Richard Knutsson   [PATCH] fs/partit...
962
  		return false;
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
963
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
964
  	part = &vb->vblk.part;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
965
966
  	part->start = get_unaligned_be64(buffer + 0x24 + r_name);
  	part->volume_offset = get_unaligned_be64(buffer + 0x2C + r_name);
dde33348e   Anton Altaparmakov   LDM: Fix for Wind...
967
968
969
  	part->size = ldm_get_vnum(buffer + 0x34 + r_name);
  	part->parent_id = ldm_get_vnum(buffer + 0x34 + r_size);
  	part->disk_id = ldm_get_vnum(buffer + 0x34 + r_parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
970
971
972
973
  	if (vb->flags & VBLK_FLAG_PART_INDEX)
  		part->partnum = buffer[0x35 + r_diskid];
  	else
  		part->partnum = 0;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
974
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
976
977
978
979
980
981
982
983
984
  }
  
  /**
   * ldm_parse_vol5 - Read a raw VBLK Volume object into a vblk structure
   * @buffer:  Block of data being worked on
   * @buflen:  Size of the block of data
   * @vb:      In-memory vblk in which to return information
   *
   * Read a raw VBLK Volume object (version 5) into a vblk structure.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
985
986
   * Return:  'true'   @vb contains a Volume VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
987
   */
959bc220d   Anton Altaparmakov   Fix LDM for new f...
988
  static bool ldm_parse_vol5(const u8 *buffer, int buflen, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
  {
959bc220d   Anton Altaparmakov   Fix LDM for new f...
990
991
  	int r_objid, r_name, r_vtype, r_disable_drive_letter, r_child, r_size;
  	int r_id1, r_id2, r_size2, r_drive, len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
  	struct vblk_volu *volu;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
  	BUG_ON(!buffer || !vb);
  	r_objid = ldm_relative(buffer, buflen, 0x18, 0);
  	if (r_objid < 0) {
  		ldm_error("r_objid %d < 0", r_objid);
  		return false;
  	}
  	r_name = ldm_relative(buffer, buflen, 0x18, r_objid);
  	if (r_name < 0) {
  		ldm_error("r_name %d < 0", r_name);
  		return false;
  	}
  	r_vtype = ldm_relative(buffer, buflen, 0x18, r_name);
  	if (r_vtype < 0) {
  		ldm_error("r_vtype %d < 0", r_vtype);
  		return false;
  	}
  	r_disable_drive_letter = ldm_relative(buffer, buflen, 0x18, r_vtype);
  	if (r_disable_drive_letter < 0) {
  		ldm_error("r_disable_drive_letter %d < 0",
  				r_disable_drive_letter);
  		return false;
  	}
  	r_child = ldm_relative(buffer, buflen, 0x2D, r_disable_drive_letter);
  	if (r_child < 0) {
  		ldm_error("r_child %d < 0", r_child);
  		return false;
  	}
  	r_size = ldm_relative(buffer, buflen, 0x3D, r_child);
  	if (r_size < 0) {
  		ldm_error("r_size %d < 0", r_size);
  		return false;
  	}
  	if (buffer[0x12] & VBLK_FLAG_VOLU_ID1) {
  		r_id1 = ldm_relative(buffer, buflen, 0x52, r_size);
  		if (r_id1 < 0) {
  			ldm_error("r_id1 %d < 0", r_id1);
  			return false;
  		}
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
  		r_id1 = r_size;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1033
1034
1035
1036
1037
1038
1039
  	if (buffer[0x12] & VBLK_FLAG_VOLU_ID2) {
  		r_id2 = ldm_relative(buffer, buflen, 0x52, r_id1);
  		if (r_id2 < 0) {
  			ldm_error("r_id2 %d < 0", r_id2);
  			return false;
  		}
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
  		r_id2 = r_id1;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1041
1042
1043
1044
1045
1046
1047
  	if (buffer[0x12] & VBLK_FLAG_VOLU_SIZE) {
  		r_size2 = ldm_relative(buffer, buflen, 0x52, r_id2);
  		if (r_size2 < 0) {
  			ldm_error("r_size2 %d < 0", r_size2);
  			return false;
  		}
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
  		r_size2 = r_id2;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1049
1050
1051
1052
1053
1054
1055
  	if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) {
  		r_drive = ldm_relative(buffer, buflen, 0x52, r_size2);
  		if (r_drive < 0) {
  			ldm_error("r_drive %d < 0", r_drive);
  			return false;
  		}
  	} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1056
  		r_drive = r_size2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  	len = r_drive;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1058
1059
  	if (len < 0) {
  		ldm_error("len %d < 0", len);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1060
  		return false;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1061
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  	len += VBLK_SIZE_VOL5;
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
1063
  	if (len > get_unaligned_be32(buffer + 0x14)) {
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1064
  		ldm_error("len %d > BE32(buffer + 0x14) %d", len,
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
1065
  				get_unaligned_be32(buffer + 0x14));
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1066
  		return false;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1067
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1068
  	volu = &vb->vblk.volu;
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1069
1070
1071
1072
1073
1074
1075
  	ldm_get_vstr(buffer + 0x18 + r_name, volu->volume_type,
  			sizeof(volu->volume_type));
  	memcpy(volu->volume_state, buffer + 0x18 + r_disable_drive_letter,
  			sizeof(volu->volume_state));
  	volu->size = ldm_get_vnum(buffer + 0x3D + r_child);
  	volu->partition_type = buffer[0x41 + r_size];
  	memcpy(volu->guid, buffer + 0x42 + r_size, sizeof(volu->guid));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
  	if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) {
959bc220d   Anton Altaparmakov   Fix LDM for new f...
1077
1078
  		ldm_get_vstr(buffer + 0x52 + r_size, volu->drive_hint,
  				sizeof(volu->drive_hint));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1079
  	}
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1080
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
  }
  
  /**
   * ldm_parse_vblk - Read a raw VBLK object into a vblk structure
   * @buf:  Block of data being worked on
   * @len:  Size of the block of data
   * @vb:   In-memory vblk in which to return information
   *
   * Read a raw VBLK object into a vblk structure.  This function just reads the
   * information common to all VBLK types, then delegates the rest of the work to
   * helper functions: ldm_parse_*.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1093
1094
   * Return:  'true'   @vb contains a VBLK
   *          'false'  @vb contents are not defined
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1095
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1096
  static bool ldm_parse_vblk (const u8 *buf, int len, struct vblk *vb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
  {
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1098
  	bool result = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1099
1100
1101
1102
1103
1104
1105
  	int r_objid;
  
  	BUG_ON (!buf || !vb);
  
  	r_objid = ldm_relative (buf, len, 0x18, 0);
  	if (r_objid < 0) {
  		ldm_error ("VBLK header is corrupt.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1106
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
  	}
  
  	vb->flags  = buf[0x12];
  	vb->type   = buf[0x13];
  	vb->obj_id = ldm_get_vnum (buf + 0x18);
  	ldm_get_vstr (buf+0x18+r_objid, vb->name, sizeof (vb->name));
  
  	switch (vb->type) {
  		case VBLK_CMP3:  result = ldm_parse_cmp3 (buf, len, vb); break;
  		case VBLK_DSK3:  result = ldm_parse_dsk3 (buf, len, vb); break;
  		case VBLK_DSK4:  result = ldm_parse_dsk4 (buf, len, vb); break;
  		case VBLK_DGR3:  result = ldm_parse_dgr3 (buf, len, vb); break;
  		case VBLK_DGR4:  result = ldm_parse_dgr4 (buf, len, vb); break;
  		case VBLK_PRT3:  result = ldm_parse_prt3 (buf, len, vb); break;
  		case VBLK_VOL5:  result = ldm_parse_vol5 (buf, len, vb); break;
  	}
  
  	if (result)
  		ldm_debug ("Parsed VBLK 0x%llx (type: 0x%02x) ok.",
  			 (unsigned long long) vb->obj_id, vb->type);
  	else
  		ldm_error ("Failed to parse VBLK 0x%llx (type: 0x%02x).",
  			(unsigned long long) vb->obj_id, vb->type);
  
  	return result;
  }
  
  
  /**
   * ldm_ldmdb_add - Adds a raw VBLK entry to the ldmdb database
   * @data:  Raw VBLK to add to the database
   * @len:   Size of the raw VBLK
   * @ldb:   Cache of the database structures
   *
   * The VBLKs are sorted into categories.  Partitions are also sorted by offset.
   *
   * N.B.  This function does not check the validity of the VBLKs.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1145
1146
   * Return:  'true'   The VBLK was added
   *          'false'  An error occurred
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1147
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1148
  static bool ldm_ldmdb_add (u8 *data, int len, struct ldmdb *ldb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1149
1150
1151
1152
1153
1154
1155
1156
1157
  {
  	struct vblk *vb;
  	struct list_head *item;
  
  	BUG_ON (!data || !ldb);
  
  	vb = kmalloc (sizeof (*vb), GFP_KERNEL);
  	if (!vb) {
  		ldm_crit ("Out of memory.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1158
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1159
1160
1161
1162
  	}
  
  	if (!ldm_parse_vblk (data, len, vb)) {
  		kfree(vb);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1163
  		return false;			/* Already logged */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
  	}
  
  	/* Put vblk into the correct list. */
  	switch (vb->type) {
  	case VBLK_DGR3:
  	case VBLK_DGR4:
  		list_add (&vb->list, &ldb->v_dgrp);
  		break;
  	case VBLK_DSK3:
  	case VBLK_DSK4:
  		list_add (&vb->list, &ldb->v_disk);
  		break;
  	case VBLK_VOL5:
  		list_add (&vb->list, &ldb->v_volu);
  		break;
  	case VBLK_CMP3:
  		list_add (&vb->list, &ldb->v_comp);
  		break;
  	case VBLK_PRT3:
  		/* Sort by the partition's start sector. */
  		list_for_each (item, &ldb->v_part) {
  			struct vblk *v = list_entry (item, struct vblk, list);
  			if ((v->vblk.part.disk_id == vb->vblk.part.disk_id) &&
  			    (v->vblk.part.start > vb->vblk.part.start)) {
  				list_add_tail (&vb->list, &v->list);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1189
  				return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1190
1191
1192
1193
1194
  			}
  		}
  		list_add_tail (&vb->list, &ldb->v_part);
  		break;
  	}
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1195
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
  }
  
  /**
   * ldm_frag_add - Add a VBLK fragment to a list
   * @data:   Raw fragment to be added to the list
   * @size:   Size of the raw fragment
   * @frags:  Linked list of VBLK fragments
   *
   * Fragmented VBLKs may not be consecutive in the database, so they are placed
   * in a list so they can be pieced together later.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1207
1208
   * Return:  'true'   Success, the VBLK was added to the list
   *          'false'  Error, a problem occurred
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1209
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1210
  static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1211
1212
1213
1214
1215
1216
  {
  	struct frag *f;
  	struct list_head *item;
  	int rec, num, group;
  
  	BUG_ON (!data || !frags);
c340b1d64   Timo Warns   fs/partitions/ldm...
1217
1218
1219
1220
  	if (size < 2 * VBLK_SIZE_HEAD) {
  		ldm_error("Value of size is to small.");
  		return false;
  	}
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
1221
1222
1223
  	group = get_unaligned_be32(data + 0x08);
  	rec   = get_unaligned_be16(data + 0x0C);
  	num   = get_unaligned_be16(data + 0x0E);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1224
1225
  	if ((num < 1) || (num > 4)) {
  		ldm_error ("A VBLK claims to have %d parts.", num);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1226
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1227
  	}
c340b1d64   Timo Warns   fs/partitions/ldm...
1228
1229
1230
1231
  	if (rec >= num) {
  		ldm_error("REC value (%d) exceeds NUM value (%d)", rec, num);
  		return false;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
  
  	list_for_each (item, frags) {
  		f = list_entry (item, struct frag, list);
  		if (f->group == group)
  			goto found;
  	}
  
  	f = kmalloc (sizeof (*f) + size*num, GFP_KERNEL);
  	if (!f) {
  		ldm_crit ("Out of memory.");
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1242
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
1244
1245
1246
1247
1248
1249
1250
1251
  	}
  
  	f->group = group;
  	f->num   = num;
  	f->rec   = rec;
  	f->map   = 0xFF << num;
  
  	list_add_tail (&f->list, frags);
  found:
cae13fe4c   Timo Warns   Fix for buffer ov...
1252
1253
1254
1255
  	if (rec >= f->num) {
  		ldm_error("REC value (%d) exceeds NUM value (%d)", rec, f->num);
  		return false;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1256
1257
1258
  	if (f->map & (1 << rec)) {
  		ldm_error ("Duplicate VBLK, part %d.", rec);
  		f->map &= 0x7F;			/* Mark the group as broken */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1259
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1260
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1261
  	f->map |= (1 << rec);
97387e3ba   Anton Altaparmakov   LDM: Fix reassemb...
1262
1263
  	if (!rec)
  		memcpy(f->data, data, VBLK_SIZE_HEAD);
c340b1d64   Timo Warns   fs/partitions/ldm...
1264
1265
  	data += VBLK_SIZE_HEAD;
  	size -= VBLK_SIZE_HEAD;
97387e3ba   Anton Altaparmakov   LDM: Fix reassemb...
1266
  	memcpy(f->data + VBLK_SIZE_HEAD + rec * size, data, size);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1267
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
  }
  
  /**
   * ldm_frag_free - Free a linked list of VBLK fragments
   * @list:  Linked list of fragments
   *
   * Free a linked list of VBLK fragments
   *
   * Return:  none
   */
  static void ldm_frag_free (struct list_head *list)
  {
  	struct list_head *item, *tmp;
  
  	BUG_ON (!list);
  
  	list_for_each_safe (item, tmp, list)
  		kfree (list_entry (item, struct frag, list));
  }
  
  /**
   * ldm_frag_commit - Validate fragmented VBLKs and add them to the database
   * @frags:  Linked list of VBLK fragments
   * @ldb:    Cache of the database structures
   *
   * Now that all the fragmented VBLKs have been collected, they must be added to
   * the database for later use.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1296
1297
   * Return:  'true'   All the fragments we added successfully
   *          'false'  One or more of the fragments we invalid
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1298
   */
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1299
  static bool ldm_frag_commit (struct list_head *frags, struct ldmdb *ldb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
  {
  	struct frag *f;
  	struct list_head *item;
  
  	BUG_ON (!frags || !ldb);
  
  	list_for_each (item, frags) {
  		f = list_entry (item, struct frag, list);
  
  		if (f->map != 0xFF) {
  			ldm_error ("VBLK group %d is incomplete (0x%02x).",
  				f->group, f->map);
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1312
  			return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1313
1314
1315
  		}
  
  		if (!ldm_ldmdb_add (f->data, f->num*ldb->vm.vblk_size, ldb))
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1316
  			return false;		/* Already logged */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1317
  	}
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1318
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1319
1320
1321
1322
  }
  
  /**
   * ldm_get_vblks - Read the on-disk database of VBLKs into memory
1493bf217   Tejun Heo   block: use struct...
1323
1324
   * @state: Partition check state including device holding the LDM Database
   * @base:  Offset, into @state->bdev, of the database
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1325
1326
1327
1328
1329
   * @ldb:   Cache of the database structures
   *
   * To use the information from the VBLKs, they need to be read from the disk,
   * unpacked and validated.  We cache them in @ldb according to their type.
   *
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1330
1331
   * Return:  'true'   All the VBLKs were read successfully
   *          'false'  An error occurred
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1332
   */
1493bf217   Tejun Heo   block: use struct...
1333
1334
  static bool ldm_get_vblks(struct parsed_partitions *state, unsigned long base,
  			  struct ldmdb *ldb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1335
1336
1337
1338
  {
  	int size, perbuf, skip, finish, s, v, recs;
  	u8 *data = NULL;
  	Sector sect;
130c6b989   Richard Knutsson   [PATCH] fs/partit...
1339
  	bool result = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1340
  	LIST_HEAD (frags);
1493bf217   Tejun Heo   block: use struct...
1341
  	BUG_ON(!state || !ldb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342
1343
1344
1345
1346
1347
1348
  
  	size   = ldb->vm.vblk_size;
  	perbuf = 512 / size;
  	skip   = ldb->vm.vblk_offset >> 9;		/* Bytes to sectors */
  	finish = (size * ldb->vm.last_vblk_seq) >> 9;
  
  	for (s = skip; s < finish; s++) {		/* For each sector */
1493bf217   Tejun Heo   block: use struct...
1349
  		data = read_part_sector(state, base + OFF_VMDB + s, &sect);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1350
1351
1352
1353
1354
1355
  		if (!data) {
  			ldm_crit ("Disk read failed.");
  			goto out;
  		}
  
  		for (v = 0; v < perbuf; v++, data+=size) {  /* For each vblk */
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
1356
  			if (MAGIC_VBLK != get_unaligned_be32(data)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1357
1358
1359
  				ldm_error ("Expected to find a VBLK.");
  				goto out;
  			}
b7bbf8fa6   Harvey Harrison   fs: ldm.[ch] use ...
1360
  			recs = get_unaligned_be16(data + 0x0E);	/* Number of records */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
  			if (recs == 1) {
  				if (!ldm_ldmdb_add (data, size, ldb))
  					goto out;	/* Already logged */
  			} else if (recs > 1) {
  				if (!ldm_frag_add (data, size, &frags))
  					goto out;	/* Already logged */
  			}
  			/* else Record is not in use, ignore it. */
  		}
  		put_dev_sector (sect);
  		data = NULL;
  	}
  
  	result = ldm_frag_commit (&frags, ldb);	/* Failures, already logged */
  out:
  	if (data)
  		put_dev_sector (sect);
  	ldm_frag_free (&frags);
  
  	return result;
  }
  
  /**
   * ldm_free_vblks - Free a linked list of vblk's
   * @lh:  Head of a linked list of struct vblk
   *
   * Free a list of vblk's and free the memory used to maintain the list.
   *
   * Return:  none
   */
  static void ldm_free_vblks (struct list_head *lh)
  {
  	struct list_head *item, *tmp;
  
  	BUG_ON (!lh);
  
  	list_for_each_safe (item, tmp, lh)
  		kfree (list_entry (item, struct vblk, list));
  }
  
  
  /**
   * ldm_partition - Find out whether a device is a dynamic disk and handle it
1493bf217   Tejun Heo   block: use struct...
1404
   * @state: Partition check state including device holding the LDM Database
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1405
1406
1407
1408
1409
1410
1411
1412
1413
   *
   * This determines whether the device @bdev is a dynamic disk and if so creates
   * the partitions necessary in the gendisk structure pointed to by @hd.
   *
   * We create a dummy device 1, which contains the LDM database, and then create
   * each partition described by the LDM database in sequence as devices 2+. For
   * example, if the device is hda, we would have: hda1: LDM database, hda2, hda3,
   * and so on: the actual data containing partitions.
   *
1493bf217   Tejun Heo   block: use struct...
1414
1415
   * Return:  1 Success, @state->bdev is a dynamic disk and we handled it
   *          0 Success, @state->bdev is not a dynamic disk
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1416
   *         -1 An error occurred before enough information had been read
1493bf217   Tejun Heo   block: use struct...
1417
   *            Or @state->bdev is a dynamic disk, but it may be corrupted
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1418
   */
1493bf217   Tejun Heo   block: use struct...
1419
  int ldm_partition(struct parsed_partitions *state)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1420
1421
1422
1423
  {
  	struct ldmdb  *ldb;
  	unsigned long base;
  	int result = -1;
1493bf217   Tejun Heo   block: use struct...
1424
  	BUG_ON(!state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
1426
  
  	/* Look for signs of a Dynamic Disk */
1493bf217   Tejun Heo   block: use struct...
1427
  	if (!ldm_validate_partition_table(state))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1428
1429
1430
1431
1432
1433
1434
1435
1436
  		return 0;
  
  	ldb = kmalloc (sizeof (*ldb), GFP_KERNEL);
  	if (!ldb) {
  		ldm_crit ("Out of memory.");
  		goto out;
  	}
  
  	/* Parse and check privheads. */
1493bf217   Tejun Heo   block: use struct...
1437
  	if (!ldm_validate_privheads(state, &ldb->ph))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1438
1439
1440
1441
1442
1443
  		goto out;		/* Already logged */
  
  	/* All further references are relative to base (database start). */
  	base = ldb->ph.config_start;
  
  	/* Parse and check tocs and vmdb. */
1493bf217   Tejun Heo   block: use struct...
1444
1445
  	if (!ldm_validate_tocblocks(state, base, ldb) ||
  	    !ldm_validate_vmdb(state, base, ldb))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1446
1447
1448
1449
1450
1451
1452
1453
  	    	goto out;		/* Already logged */
  
  	/* Initialize vblk lists in ldmdb struct */
  	INIT_LIST_HEAD (&ldb->v_dgrp);
  	INIT_LIST_HEAD (&ldb->v_disk);
  	INIT_LIST_HEAD (&ldb->v_volu);
  	INIT_LIST_HEAD (&ldb->v_comp);
  	INIT_LIST_HEAD (&ldb->v_part);
1493bf217   Tejun Heo   block: use struct...
1454
  	if (!ldm_get_vblks(state, base, ldb)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1455
1456
1457
1458
1459
  		ldm_crit ("Failed to read the VBLKs from the database.");
  		goto cleanup;
  	}
  
  	/* Finally, create the data partition devices. */
1493bf217   Tejun Heo   block: use struct...
1460
  	if (ldm_create_data_partitions(state, ldb)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
  		ldm_debug ("Parsed LDM database successfully.");
  		result = 1;
  	}
  	/* else Already logged */
  
  cleanup:
  	ldm_free_vblks (&ldb->v_dgrp);
  	ldm_free_vblks (&ldb->v_disk);
  	ldm_free_vblks (&ldb->v_volu);
  	ldm_free_vblks (&ldb->v_comp);
  	ldm_free_vblks (&ldb->v_part);
  out:
  	kfree (ldb);
  	return result;
  }