diff options
author | Anton Altaparmakov <aia21@cantab.net> | 2007-05-21 04:37:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-21 12:58:40 -0400 |
commit | dde33348e53ecab687a9768bf5262f0b8f79b7f2 (patch) | |
tree | 6bfb828e56147b7c327a1304c27e93fc92f6c060 /fs/partitions/ldm.c | |
parent | 17304383ebc1ce68a88030ac4d18ea549d9578c7 (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.c | 206 |
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 | */ |
133 | static bool ldm_parse_privhead (const u8 *data, struct privhead *ph) | 131 | static 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 | */ |
412 | static bool ldm_validate_tocblocks (struct block_device *bdev, | 412 | static 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], §); | 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], §); | ||
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; |
464 | out: | 469 | err: |
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 | */ |
978 | static bool ldm_parse_prt3 (const u8 *buffer, int buflen, struct vblk *vb) | 981 | static 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 | |||