aboutsummaryrefslogtreecommitdiffstats
path: root/fs/partitions/ldm.c
diff options
context:
space:
mode:
authorAnton Altaparmakov <aia21@cantab.net>2007-05-21 04:37:42 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-21 12:58:40 -0400
commitdde33348e53ecab687a9768bf5262f0b8f79b7f2 (patch)
tree6bfb828e56147b7c327a1304c27e93fc92f6c060 /fs/partitions/ldm.c
parent17304383ebc1ce68a88030ac4d18ea549d9578c7 (diff)
LDM: Fix for Windows Vista dynamic disks
This fixes the LDM driver so that it works with Windows Vista dynamic disks which are subtly different to Windows 2000/XP ones. The patch was needed to get a Vista formatted dynamic disk to be recognized and parsed successfully. Thanks go to Chris Teachworth for the report and testing. Cc: Richard Russon <ldm@flatcap.org> Signed-off-by: Anton Altaparmakov <aia21@cantab.net> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/partitions/ldm.c')
-rw-r--r--fs/partitions/ldm.c206
1 files changed, 116 insertions, 90 deletions
diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c
index 1a60926a4ccd..c387812537d7 100644
--- a/fs/partitions/ldm.c
+++ b/fs/partitions/ldm.c
@@ -2,10 +2,10 @@
2 * ldm - Support for Windows Logical Disk Manager (Dynamic Disks) 2 * ldm - Support for Windows Logical Disk Manager (Dynamic Disks)
3 * 3 *
4 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 4 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
5 * Copyright (c) 2001-2004 Anton Altaparmakov 5 * Copyright (c) 2001-2007 Anton Altaparmakov
6 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 6 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
7 * 7 *
8 * Documentation is available at http://linux-ntfs.sf.net/ldm 8 * Documentation is available at http://www.linux-ntfs.org/content/view/19/37/
9 * 9 *
10 * This program is free software; you can redistribute it and/or modify it under 10 * This program is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software 11 * the terms of the GNU General Public License as published by the Free Software
@@ -62,7 +62,6 @@ static void _ldm_printk (const char *level, const char *function,
62 printk ("%s%s(): %s\n", level, function, buf); 62 printk ("%s%s(): %s\n", level, function, buf);
63} 63}
64 64
65
66/** 65/**
67 * ldm_parse_hexbyte - Convert a ASCII hex number to a byte 66 * ldm_parse_hexbyte - Convert a ASCII hex number to a byte
68 * @src: Pointer to at least 2 characters to convert. 67 * @src: Pointer to at least 2 characters to convert.
@@ -118,7 +117,6 @@ static bool ldm_parse_guid (const u8 *src, u8 *dest)
118 return true; 117 return true;
119} 118}
120 119
121
122/** 120/**
123 * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure 121 * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure
124 * @data: Raw database PRIVHEAD structure loaded from the device 122 * @data: Raw database PRIVHEAD structure loaded from the device
@@ -130,46 +128,48 @@ static bool ldm_parse_guid (const u8 *src, u8 *dest)
130 * Return: 'true' @ph contains the PRIVHEAD data 128 * Return: 'true' @ph contains the PRIVHEAD data
131 * 'false' @ph contents are undefined 129 * 'false' @ph contents are undefined
132 */ 130 */
133static bool ldm_parse_privhead (const u8 *data, struct privhead *ph) 131static bool ldm_parse_privhead(const u8 *data, struct privhead *ph)
134{ 132{
135 BUG_ON (!data || !ph); 133 bool is_vista = false;
136 134
137 if (MAGIC_PRIVHEAD != BE64 (data)) { 135 BUG_ON(!data || !ph);
138 ldm_error ("Cannot find PRIVHEAD structure. LDM database is" 136 if (MAGIC_PRIVHEAD != BE64(data)) {
137 ldm_error("Cannot find PRIVHEAD structure. LDM database is"
139 " corrupt. Aborting."); 138 " corrupt. Aborting.");
140 return false; 139 return false;
141 } 140 }
142 141 ph->ver_major = BE16(data + 0x000C);
143 ph->ver_major = BE16 (data + 0x000C); 142 ph->ver_minor = BE16(data + 0x000E);
144 ph->ver_minor = BE16 (data + 0x000E); 143 ph->logical_disk_start = BE64(data + 0x011B);
145 ph->logical_disk_start = BE64 (data + 0x011B); 144 ph->logical_disk_size = BE64(data + 0x0123);
146 ph->logical_disk_size = BE64 (data + 0x0123); 145 ph->config_start = BE64(data + 0x012B);
147 ph->config_start = BE64 (data + 0x012B); 146 ph->config_size = BE64(data + 0x0133);
148 ph->config_size = BE64 (data + 0x0133); 147 /* Version 2.11 is Win2k/XP and version 2.12 is Vista. */
149 148 if (ph->ver_major == 2 && ph->ver_minor == 12)
150 if ((ph->ver_major != 2) || (ph->ver_minor != 11)) { 149 is_vista = true;
151 ldm_error ("Expected PRIVHEAD version %d.%d, got %d.%d." 150 if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) {
152 " Aborting.", 2, 11, ph->ver_major, ph->ver_minor); 151 ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d."
152 " Aborting.", ph->ver_major, ph->ver_minor);
153 return false; 153 return false;
154 } 154 }
155 ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major,
156 ph->ver_minor, is_vista ? "Vista" : "2000/XP");
155 if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */ 157 if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */
156 /* Warn the user and continue, carefully */ 158 /* Warn the user and continue, carefully. */
157 ldm_info ("Database is normally %u bytes, it claims to " 159 ldm_info("Database is normally %u bytes, it claims to "
158 "be %llu bytes.", LDM_DB_SIZE, 160 "be %llu bytes.", LDM_DB_SIZE,
159 (unsigned long long)ph->config_size ); 161 udunsigned long long)ph->config_size);
160 } 162 }
161 if ((ph->logical_disk_size == 0) || 163 if ((ph->logical_disk_size == 0) || (ph->logical_disk_start +
162 (ph->logical_disk_start + ph->logical_disk_size > ph->config_start)) { 164 ph->logical_disk_size > ph->config_start)) {
163 ldm_error ("PRIVHEAD disk size doesn't match real disk size"); 165 ldm_error("PRIVHEAD disk size doesn't match real disk size");
164 return false; 166 return false;
165 } 167 }
166 168 if (!ldm_parse_guid(data + 0x0030, ph->disk_id)) {
167 if (!ldm_parse_guid (data + 0x0030, ph->disk_id)) { 169 ldm_error("PRIVHEAD contains an invalid GUID.");
168 ldm_error ("PRIVHEAD contains an invalid GUID.");
169 return false; 170 return false;
170 } 171 }
171 172 ldm_debug("Parsed PRIVHEAD successfully.");
172 ldm_debug ("Parsed PRIVHEAD successfully.");
173 return true; 173 return true;
174} 174}
175 175
@@ -409,7 +409,7 @@ out:
409 * Return: 'true' @toc1 contains validated TOCBLOCK info 409 * Return: 'true' @toc1 contains validated TOCBLOCK info
410 * 'false' @toc1 contents are undefined 410 * 'false' @toc1 contents are undefined
411 */ 411 */
412static bool ldm_validate_tocblocks (struct block_device *bdev, 412static bool ldm_validate_tocblocks(struct block_device *bdev,
413 unsigned long base, struct ldmdb *ldb) 413 unsigned long base, struct ldmdb *ldb)
414{ 414{
415 static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4}; 415 static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4};
@@ -417,54 +417,57 @@ static bool ldm_validate_tocblocks (struct block_device *bdev,
417 struct privhead *ph; 417 struct privhead *ph;
418 Sector sect; 418 Sector sect;
419 u8 *data; 419 u8 *data;
420 int i, nr_tbs;
420 bool result = false; 421 bool result = false;
421 int i;
422 422
423 BUG_ON (!bdev || !ldb); 423 BUG_ON(!bdev || !ldb);
424 424 ph = &ldb->ph;
425 ph = &ldb->ph;
426 tb[0] = &ldb->toc; 425 tb[0] = &ldb->toc;
427 tb[1] = kmalloc (sizeof (*tb[1]), GFP_KERNEL); 426 tb[1] = kmalloc(sizeof(*tb[1]) * 3, GFP_KERNEL);
428 tb[2] = kmalloc (sizeof (*tb[2]), GFP_KERNEL); 427 if (!tb[1]) {
429 tb[3] = kmalloc (sizeof (*tb[3]), GFP_KERNEL); 428 ldm_crit("Out of memory.");
430 if (!tb[1] || !tb[2] || !tb[3]) { 429 goto err;
431 ldm_crit ("Out of memory.");
432 goto out;
433 } 430 }
434 431 tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1]));
435 for (i = 0; i < 4; i++) /* Read and parse all four toc's. */ 432 tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2]));
436 { 433 /*
437 data = read_dev_sector (bdev, base + off[i], &sect); 434 * Try to read and parse all four TOCBLOCKs.
435 *
436 * Windows Vista LDM v2.12 does not always have all four TOCBLOCKs so
437 * skip any that fail as long as we get at least one valid TOCBLOCK.
438 */
439 for (nr_tbs = i = 0; i < 4; i++) {
440 data = read_dev_sector(bdev, base + off[i], &sect);
438 if (!data) { 441 if (!data) {
439 ldm_crit ("Disk read failed."); 442 ldm_error("Disk read failed for TOCBLOCK %d.", i);
440 goto out; 443 continue;
441 } 444 }
442 result = ldm_parse_tocblock (data, tb[i]); 445 if (ldm_parse_tocblock(data, tb[nr_tbs]))
443 put_dev_sector (sect); 446 nr_tbs++;
444 if (!result) 447 put_dev_sector(sect);
445 goto out; /* Already logged */
446 } 448 }
447 449 if (!nr_tbs) {
448 /* Range check the toc against a privhead. */ 450 ldm_crit("Failed to find a valid TOCBLOCK.");
451 goto err;
452 }
453 /* Range check the TOCBLOCK against a privhead. */
449 if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) || 454 if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) ||
450 ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) > ph->config_size)) { 455 ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) >
451 ldm_crit ("The bitmaps are out of range. Giving up."); 456 ph->config_size)) {
452 goto out; 457 ldm_crit("The bitmaps are out of range. Giving up.");
458 goto err;
453 } 459 }
454 460 /* Compare all loaded TOCBLOCKs. */
455 if (!ldm_compare_tocblocks (tb[0], tb[1]) || /* Compare all tocs. */ 461 for (i = 1; i < nr_tbs; i++) {
456 !ldm_compare_tocblocks (tb[0], tb[2]) || 462 if (!ldm_compare_tocblocks(tb[0], tb[i])) {
457 !ldm_compare_tocblocks (tb[0], tb[3])) { 463 ldm_crit("TOCBLOCKs 0 and %d do not match.", i);
458 ldm_crit ("The TOCBLOCKs don't match."); 464 goto err;
459 goto out; 465 }
460 } 466 }
461 467 ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs);
462 ldm_debug ("Validated TOCBLOCKs successfully.");
463 result = true; 468 result = true;
464out: 469err:
465 kfree (tb[1]); 470 kfree(tb[1]);
466 kfree (tb[2]);
467 kfree (tb[3]);
468 return result; 471 return result;
469} 472}
470 473
@@ -566,7 +569,7 @@ static bool ldm_validate_partition_table (struct block_device *bdev)
566 569
567 p = (struct partition*)(data + 0x01BE); 570 p = (struct partition*)(data + 0x01BE);
568 for (i = 0; i < 4; i++, p++) 571 for (i = 0; i < 4; i++, p++)
569 if (SYS_IND (p) == WIN2K_DYNAMIC_PARTITION) { 572 if (SYS_IND (p) == LDM_PARTITION) {
570 result = true; 573 result = true;
571 break; 574 break;
572 } 575 }
@@ -975,44 +978,68 @@ static bool ldm_parse_dsk4 (const u8 *buffer, int buflen, struct vblk *vb)
975 * Return: 'true' @vb contains a Partition VBLK 978 * Return: 'true' @vb contains a Partition VBLK
976 * 'false' @vb contents are not defined 979 * 'false' @vb contents are not defined
977 */ 980 */
978static bool ldm_parse_prt3 (const u8 *buffer, int buflen, struct vblk *vb) 981static bool ldm_parse_prt3(const u8 *buffer, int buflen, struct vblk *vb)
979{ 982{
980 int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len; 983 int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len;
981 struct vblk_part *part; 984 struct vblk_part *part;
982 985
983 BUG_ON (!buffer || !vb); 986 BUG_ON(!buffer || !vb);
984 987 r_objid = ldm_relative(buffer, buflen, 0x18, 0);
985 r_objid = ldm_relative (buffer, buflen, 0x18, 0); 988 if (r_objid < 0) {
986 r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 989 ldm_error("r_objid %d < 0", r_objid);
987 r_size = ldm_relative (buffer, buflen, 0x34, r_name); 990 return false;
988 r_parent = ldm_relative (buffer, buflen, 0x34, r_size); 991 }
989 r_diskid = ldm_relative (buffer, buflen, 0x34, r_parent); 992 r_name = ldm_relative(buffer, buflen, 0x18, r_objid);
990 993 if (r_name < 0) {
994 ldm_error("r_name %d < 0", r_name);
995 return false;
996 }
997 r_size = ldm_relative(buffer, buflen, 0x34, r_name);
998 if (r_size < 0) {
999 ldm_error("r_size %d < 0", r_size);
1000 return false;
1001 }
1002 r_parent = ldm_relative(buffer, buflen, 0x34, r_size);
1003 if (r_parent < 0) {
1004 ldm_error("r_parent %d < 0", r_parent);
1005 return false;
1006 }
1007 r_diskid = ldm_relative(buffer, buflen, 0x34, r_parent);
1008 if (r_diskid < 0) {
1009 ldm_error("r_diskid %d < 0", r_diskid);
1010 return false;
1011 }
991 if (buffer[0x12] & VBLK_FLAG_PART_INDEX) { 1012 if (buffer[0x12] & VBLK_FLAG_PART_INDEX) {
992 r_index = ldm_relative (buffer, buflen, 0x34, r_diskid); 1013 r_index = ldm_relative(buffer, buflen, 0x34, r_diskid);
1014 if (r_index < 0) {
1015 ldm_error("r_index %d < 0", r_index);
1016 return false;
1017 }
993 len = r_index; 1018 len = r_index;
994 } else { 1019 } else {
995 r_index = 0; 1020 r_index = 0;
996 len = r_diskid; 1021 len = r_diskid;
997 } 1022 }
998 if (len < 0) 1023 if (len < 0) {
1024 ldm_error("len %d < 0", len);
999 return false; 1025 return false;
1000 1026 }
1001 len += VBLK_SIZE_PRT3; 1027 len += VBLK_SIZE_PRT3;
1002 if (len != BE32 (buffer + 0x14)) 1028 if (len > BE32(buffer + 0x14)) {
1029 ldm_error("len %d > BE32(buffer + 0x14) %d", len,
1030 BE32(buffer + 0x14));
1003 return false; 1031 return false;
1004 1032 }
1005 part = &vb->vblk.part; 1033 part = &vb->vblk.part;
1006 part->start = BE64 (buffer + 0x24 + r_name); 1034 part->start = BE64(buffer + 0x24 + r_name);
1007 part->volume_offset = BE64 (buffer + 0x2C + r_name); 1035 part->volume_offset = BE64(buffer + 0x2C + r_name);
1008 part->size = ldm_get_vnum (buffer + 0x34 + r_name); 1036 part->size = ldm_get_vnum(buffer + 0x34 + r_name);
1009 part->parent_id = ldm_get_vnum (buffer + 0x34 + r_size); 1037 part->parent_id = ldm_get_vnum(buffer + 0x34 + r_size);
1010 part->disk_id = ldm_get_vnum (buffer + 0x34 + r_parent); 1038 part->disk_id = ldm_get_vnum(buffer + 0x34 + r_parent);
1011 if (vb->flags & VBLK_FLAG_PART_INDEX) 1039 if (vb->flags & VBLK_FLAG_PART_INDEX)
1012 part->partnum = buffer[0x35 + r_diskid]; 1040 part->partnum = buffer[0x35 + r_diskid];
1013 else 1041 else
1014 part->partnum = 0; 1042 part->partnum = 0;
1015
1016 return true; 1043 return true;
1017} 1044}
1018 1045
@@ -1475,4 +1502,3 @@ out:
1475 kfree (ldb); 1502 kfree (ldb);
1476 return result; 1503 return result;
1477} 1504}
1478