aboutsummaryrefslogtreecommitdiffstats
path: root/fs
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
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')
-rw-r--r--fs/partitions/Kconfig12
-rw-r--r--fs/partitions/ldm.c206
-rw-r--r--fs/partitions/ldm.h6
3 files changed, 127 insertions, 97 deletions
diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig
index 7638a1c42a7d..a99acd8de353 100644
--- a/fs/partitions/Kconfig
+++ b/fs/partitions/Kconfig
@@ -166,8 +166,12 @@ config LDM_PARTITION
166 depends on PARTITION_ADVANCED 166 depends on PARTITION_ADVANCED
167 ---help--- 167 ---help---
168 Say Y here if you would like to use hard disks under Linux which 168 Say Y here if you would like to use hard disks under Linux which
169 were partitioned using Windows 2000's or XP's Logical Disk Manager. 169 were partitioned using Windows 2000's/XP's or Vista's Logical Disk
170 They are also known as "Dynamic Disks". 170 Manager. They are also known as "Dynamic Disks".
171
172 Note this driver only supports Dynamic Disks with a protective MBR
173 label, i.e. DOS partition table. It does not support GPT labelled
174 Dynamic Disks yet as can be created with Vista.
171 175
172 Windows 2000 introduced the concept of Dynamic Disks to get around 176 Windows 2000 introduced the concept of Dynamic Disks to get around
173 the limitations of the PC's partitioning scheme. The Logical Disk 177 the limitations of the PC's partitioning scheme. The Logical Disk
@@ -175,8 +179,8 @@ config LDM_PARTITION
175 mirrored, striped or RAID volumes, all without the need for 179 mirrored, striped or RAID volumes, all without the need for
176 rebooting. 180 rebooting.
177 181
178 Normal partitions are now called Basic Disks under Windows 2000 and 182 Normal partitions are now called Basic Disks under Windows 2000, XP,
179 XP. 183 and Vista.
180 184
181 For a fuller description read <file:Documentation/ldm.txt>. 185 For a fuller description read <file:Documentation/ldm.txt>.
182 186
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
diff --git a/fs/partitions/ldm.h b/fs/partitions/ldm.h
index 6e8d7952b8b5..d2e6a3046939 100644
--- a/fs/partitions/ldm.h
+++ b/fs/partitions/ldm.h
@@ -2,10 +2,10 @@
2 * ldm - Part of the Linux-NTFS project. 2 * ldm - Part of the Linux-NTFS project.
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 Anton Altaparmakov <aia21@cantab.net> 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 10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free 11 * under the terms of the GNU General Public License as published by the Free
@@ -93,7 +93,7 @@ struct parsed_partitions;
93 93
94#define OFF_VMDB 17 /* List of partitions. */ 94#define OFF_VMDB 17 /* List of partitions. */
95 95
96#define WIN2K_DYNAMIC_PARTITION 0x42 /* Formerly SFS (Landis). */ 96#define LDM_PARTITION 0x42 /* Formerly SFS (Landis). */
97 97
98#define TOC_BITMAP1 "config" /* Names of the two defined */ 98#define TOC_BITMAP1 "config" /* Names of the two defined */
99#define TOC_BITMAP2 "log" /* bitmaps in the TOCBLOCK. */ 99#define TOC_BITMAP2 "log" /* bitmaps in the TOCBLOCK. */