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>

authored by Anton Altaparmakov and committed by Linus Torvalds dde33348 17304383

+144 -107
+14 -7
Documentation/ldm.txt
··· 2 LDM - Logical Disk Manager (Dynamic Disks) 3 ------------------------------------------ 4 5 Overview 6 -------- 7 8 - Windows 2000 and XP use a new partitioning scheme. It is a complete 9 replacement for the MSDOS style partitions. It stores its information in a 10 1MiB journalled database at the end of the physical disk. The size of 11 partitions is limited only by disk space. The maximum number of partitions is ··· 26 assemble any multi-partition volumes, e.g. Stripes, RAID5. 27 28 To prevent legacy applications from repartitioning the disk, the LDM creates a 29 - dummy MSDOS partition containing one disk-sized partition. 30 31 32 Example ··· 95 More Documentation 96 ------------------ 97 98 - There is an Overview of the LDM online together with complete Technical 99 - Documentation. It can also be downloaded in html. 100 101 - http://linux-ntfs.sourceforge.net/ldm/index.html 102 - http://linux-ntfs.sourceforge.net/downloads.html 103 104 - If you have any LDM questions that aren't answered on the website, email me. 105 106 Cheers, 107 FlatCap - Richard Russon
··· 2 LDM - Logical Disk Manager (Dynamic Disks) 3 ------------------------------------------ 4 5 + Originally Written by FlatCap - Richard Russon <ldm@flatcap.org>. 6 + Last Updated by Anton Altaparmakov on 30 March 2007 for Windows Vista. 7 + 8 Overview 9 -------- 10 11 + Windows 2000, XP, and Vista use a new partitioning scheme. It is a complete 12 replacement for the MSDOS style partitions. It stores its information in a 13 1MiB journalled database at the end of the physical disk. The size of 14 partitions is limited only by disk space. The maximum number of partitions is ··· 23 assemble any multi-partition volumes, e.g. Stripes, RAID5. 24 25 To prevent legacy applications from repartitioning the disk, the LDM creates a 26 + dummy MSDOS partition containing one disk-sized partition. This is what is 27 + supported with the Linux LDM driver. 28 + 29 + A newer approach that has been implemented with Vista is to put LDM on top of a 30 + GPT label disk. This is not supported by the Linux LDM driver yet. 31 32 33 Example ··· 88 More Documentation 89 ------------------ 90 91 + There is an Overview of the LDM together with complete Technical Documentation. 92 + It is available for download. 93 94 + http://www.linux-ntfs.org/content/view/19/37/ 95 96 + If you have any LDM questions that aren't answered in the documentation, email 97 + me. 98 99 Cheers, 100 FlatCap - Richard Russon
+3 -3
MAINTAINERS
··· 2231 L: lm-sensors@lm-sensors.org 2232 S: Maintained 2233 2234 - LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) 2235 P: Richard Russon (FlatCap) 2236 M: ldm@flatcap.org 2237 - L: ldm-devel@lists.sourceforge.net 2238 - W: http://ldm.sourceforge.net 2239 S: Maintained 2240 2241 LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
··· 2231 L: lm-sensors@lm-sensors.org 2232 S: Maintained 2233 2234 + LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP/Vista Dynamic Disks) 2235 P: Richard Russon (FlatCap) 2236 M: ldm@flatcap.org 2237 + L: linux-ntfs-dev@lists.sourceforge.net 2238 + W: http://www.linux-ntfs.org/content/view/19/37/ 2239 S: Maintained 2240 2241 LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
+8 -4
fs/partitions/Kconfig
··· 166 depends on PARTITION_ADVANCED 167 ---help--- 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. 170 - They are also known as "Dynamic Disks". 171 172 Windows 2000 introduced the concept of Dynamic Disks to get around 173 the limitations of the PC's partitioning scheme. The Logical Disk ··· 179 mirrored, striped or RAID volumes, all without the need for 180 rebooting. 181 182 - Normal partitions are now called Basic Disks under Windows 2000 and 183 - XP. 184 185 For a fuller description read <file:Documentation/ldm.txt>. 186
··· 166 depends on PARTITION_ADVANCED 167 ---help--- 168 Say Y here if you would like to use hard disks under Linux which 169 + were partitioned using Windows 2000's/XP's or Vista's Logical Disk 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. 175 176 Windows 2000 introduced the concept of Dynamic Disks to get around 177 the limitations of the PC's partitioning scheme. The Logical Disk ··· 175 mirrored, striped or RAID volumes, all without the need for 176 rebooting. 177 178 + Normal partitions are now called Basic Disks under Windows 2000, XP, 179 + and Vista. 180 181 For a fuller description read <file:Documentation/ldm.txt>. 182
+116 -90
fs/partitions/ldm.c
··· 2 * ldm - Support for Windows Logical Disk Manager (Dynamic Disks) 3 * 4 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 5 - * Copyright (c) 2001-2004 Anton Altaparmakov 6 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 7 * 8 - * Documentation is available at http://linux-ntfs.sf.net/ldm 9 * 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 ··· 62 printk ("%s%s(): %s\n", level, function, buf); 63 } 64 65 - 66 /** 67 * ldm_parse_hexbyte - Convert a ASCII hex number to a byte 68 * @src: Pointer to at least 2 characters to convert. ··· 117 return true; 118 } 119 120 - 121 /** 122 * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure 123 * @data: Raw database PRIVHEAD structure loaded from the device ··· 128 * Return: 'true' @ph contains the PRIVHEAD data 129 * 'false' @ph contents are undefined 130 */ 131 - static bool ldm_parse_privhead (const u8 *data, struct privhead *ph) 132 { 133 - BUG_ON (!data || !ph); 134 135 - if (MAGIC_PRIVHEAD != BE64 (data)) { 136 - ldm_error ("Cannot find PRIVHEAD structure. LDM database is" 137 " corrupt. Aborting."); 138 return false; 139 } 140 - 141 - ph->ver_major = BE16 (data + 0x000C); 142 - ph->ver_minor = BE16 (data + 0x000E); 143 - ph->logical_disk_start = BE64 (data + 0x011B); 144 - ph->logical_disk_size = BE64 (data + 0x0123); 145 - ph->config_start = BE64 (data + 0x012B); 146 - ph->config_size = BE64 (data + 0x0133); 147 - 148 - if ((ph->ver_major != 2) || (ph->ver_minor != 11)) { 149 - ldm_error ("Expected PRIVHEAD version %d.%d, got %d.%d." 150 - " Aborting.", 2, 11, ph->ver_major, ph->ver_minor); 151 return false; 152 } 153 if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */ 154 - /* Warn the user and continue, carefully */ 155 - ldm_info ("Database is normally %u bytes, it claims to " 156 "be %llu bytes.", LDM_DB_SIZE, 157 - (unsigned long long)ph->config_size ); 158 } 159 - if ((ph->logical_disk_size == 0) || 160 - (ph->logical_disk_start + ph->logical_disk_size > ph->config_start)) { 161 - ldm_error ("PRIVHEAD disk size doesn't match real disk size"); 162 return false; 163 } 164 - 165 - if (!ldm_parse_guid (data + 0x0030, ph->disk_id)) { 166 - ldm_error ("PRIVHEAD contains an invalid GUID."); 167 return false; 168 } 169 - 170 - ldm_debug ("Parsed PRIVHEAD successfully."); 171 return true; 172 } 173 ··· 409 * Return: 'true' @toc1 contains validated TOCBLOCK info 410 * 'false' @toc1 contents are undefined 411 */ 412 - static bool ldm_validate_tocblocks (struct block_device *bdev, 413 unsigned long base, struct ldmdb *ldb) 414 { 415 static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4}; ··· 417 struct privhead *ph; 418 Sector sect; 419 u8 *data; 420 bool result = false; 421 - int i; 422 423 - BUG_ON (!bdev || !ldb); 424 - 425 - ph = &ldb->ph; 426 tb[0] = &ldb->toc; 427 - tb[1] = kmalloc (sizeof (*tb[1]), GFP_KERNEL); 428 - tb[2] = kmalloc (sizeof (*tb[2]), GFP_KERNEL); 429 - tb[3] = kmalloc (sizeof (*tb[3]), GFP_KERNEL); 430 - if (!tb[1] || !tb[2] || !tb[3]) { 431 - ldm_crit ("Out of memory."); 432 - goto out; 433 } 434 - 435 - for (i = 0; i < 4; i++) /* Read and parse all four toc's. */ 436 - { 437 - data = read_dev_sector (bdev, base + off[i], &sect); 438 if (!data) { 439 - ldm_crit ("Disk read failed."); 440 - goto out; 441 } 442 - result = ldm_parse_tocblock (data, tb[i]); 443 - put_dev_sector (sect); 444 - if (!result) 445 - goto out; /* Already logged */ 446 } 447 - 448 - /* Range check the toc against a privhead. */ 449 if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) || 450 - ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) > ph->config_size)) { 451 - ldm_crit ("The bitmaps are out of range. Giving up."); 452 - goto out; 453 } 454 - 455 - if (!ldm_compare_tocblocks (tb[0], tb[1]) || /* Compare all tocs. */ 456 - !ldm_compare_tocblocks (tb[0], tb[2]) || 457 - !ldm_compare_tocblocks (tb[0], tb[3])) { 458 - ldm_crit ("The TOCBLOCKs don't match."); 459 - goto out; 460 } 461 - 462 - ldm_debug ("Validated TOCBLOCKs successfully."); 463 result = true; 464 - out: 465 - kfree (tb[1]); 466 - kfree (tb[2]); 467 - kfree (tb[3]); 468 return result; 469 } 470 ··· 569 570 p = (struct partition*)(data + 0x01BE); 571 for (i = 0; i < 4; i++, p++) 572 - if (SYS_IND (p) == WIN2K_DYNAMIC_PARTITION) { 573 result = true; 574 break; 575 } ··· 978 * Return: 'true' @vb contains a Partition VBLK 979 * 'false' @vb contents are not defined 980 */ 981 - static bool ldm_parse_prt3 (const u8 *buffer, int buflen, struct vblk *vb) 982 { 983 int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len; 984 struct vblk_part *part; 985 986 - BUG_ON (!buffer || !vb); 987 - 988 - r_objid = ldm_relative (buffer, buflen, 0x18, 0); 989 - r_name = ldm_relative (buffer, buflen, 0x18, r_objid); 990 - r_size = ldm_relative (buffer, buflen, 0x34, r_name); 991 - r_parent = ldm_relative (buffer, buflen, 0x34, r_size); 992 - r_diskid = ldm_relative (buffer, buflen, 0x34, r_parent); 993 - 994 if (buffer[0x12] & VBLK_FLAG_PART_INDEX) { 995 - r_index = ldm_relative (buffer, buflen, 0x34, r_diskid); 996 len = r_index; 997 } else { 998 r_index = 0; 999 len = r_diskid; 1000 } 1001 - if (len < 0) 1002 return false; 1003 - 1004 len += VBLK_SIZE_PRT3; 1005 - if (len != BE32 (buffer + 0x14)) 1006 return false; 1007 - 1008 part = &vb->vblk.part; 1009 - part->start = BE64 (buffer + 0x24 + r_name); 1010 - part->volume_offset = BE64 (buffer + 0x2C + r_name); 1011 - part->size = ldm_get_vnum (buffer + 0x34 + r_name); 1012 - part->parent_id = ldm_get_vnum (buffer + 0x34 + r_size); 1013 - part->disk_id = ldm_get_vnum (buffer + 0x34 + r_parent); 1014 if (vb->flags & VBLK_FLAG_PART_INDEX) 1015 part->partnum = buffer[0x35 + r_diskid]; 1016 else 1017 part->partnum = 0; 1018 - 1019 return true; 1020 } 1021 ··· 1502 kfree (ldb); 1503 return result; 1504 } 1505 -
··· 2 * ldm - Support for Windows Logical Disk Manager (Dynamic Disks) 3 * 4 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 5 + * Copyright (c) 2001-2007 Anton Altaparmakov 6 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 7 * 8 + * Documentation is available at http://www.linux-ntfs.org/content/view/19/37/ 9 * 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 ··· 62 printk ("%s%s(): %s\n", level, function, buf); 63 } 64 65 /** 66 * ldm_parse_hexbyte - Convert a ASCII hex number to a byte 67 * @src: Pointer to at least 2 characters to convert. ··· 118 return true; 119 } 120 121 /** 122 * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure 123 * @data: Raw database PRIVHEAD structure loaded from the device ··· 130 * Return: 'true' @ph contains the PRIVHEAD data 131 * 'false' @ph contents are undefined 132 */ 133 + static bool ldm_parse_privhead(const u8 *data, struct privhead *ph) 134 { 135 + bool is_vista = false; 136 137 + BUG_ON(!data || !ph); 138 + if (MAGIC_PRIVHEAD != BE64(data)) { 139 + ldm_error("Cannot find PRIVHEAD structure. LDM database is" 140 " corrupt. Aborting."); 141 return false; 142 } 143 + ph->ver_major = BE16(data + 0x000C); 144 + ph->ver_minor = BE16(data + 0x000E); 145 + ph->logical_disk_start = BE64(data + 0x011B); 146 + ph->logical_disk_size = BE64(data + 0x0123); 147 + ph->config_start = BE64(data + 0x012B); 148 + ph->config_size = BE64(data + 0x0133); 149 + /* Version 2.11 is Win2k/XP and version 2.12 is Vista. */ 150 + if (ph->ver_major == 2 && ph->ver_minor == 12) 151 + is_vista = true; 152 + if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) { 153 + ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d." 154 + " Aborting.", ph->ver_major, ph->ver_minor); 155 return false; 156 } 157 + ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major, 158 + ph->ver_minor, is_vista ? "Vista" : "2000/XP"); 159 if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */ 160 + /* Warn the user and continue, carefully. */ 161 + ldm_info("Database is normally %u bytes, it claims to " 162 "be %llu bytes.", LDM_DB_SIZE, 163 + udunsigned long long)ph->config_size); 164 } 165 + if ((ph->logical_disk_size == 0) || (ph->logical_disk_start + 166 + ph->logical_disk_size > ph->config_start)) { 167 + ldm_error("PRIVHEAD disk size doesn't match real disk size"); 168 return false; 169 } 170 + if (!ldm_parse_guid(data + 0x0030, ph->disk_id)) { 171 + ldm_error("PRIVHEAD contains an invalid GUID."); 172 return false; 173 } 174 + ldm_debug("Parsed PRIVHEAD successfully."); 175 return true; 176 } 177 ··· 409 * Return: 'true' @toc1 contains validated TOCBLOCK info 410 * 'false' @toc1 contents are undefined 411 */ 412 + static bool ldm_validate_tocblocks(struct block_device *bdev, 413 unsigned long base, struct ldmdb *ldb) 414 { 415 static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4}; ··· 417 struct privhead *ph; 418 Sector sect; 419 u8 *data; 420 + int i, nr_tbs; 421 bool result = false; 422 423 + BUG_ON(!bdev || !ldb); 424 + ph = &ldb->ph; 425 tb[0] = &ldb->toc; 426 + tb[1] = kmalloc(sizeof(*tb[1]) * 3, GFP_KERNEL); 427 + if (!tb[1]) { 428 + ldm_crit("Out of memory."); 429 + goto err; 430 } 431 + tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1])); 432 + tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2])); 433 + /* 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); 441 if (!data) { 442 + ldm_error("Disk read failed for TOCBLOCK %d.", i); 443 + continue; 444 } 445 + if (ldm_parse_tocblock(data, tb[nr_tbs])) 446 + nr_tbs++; 447 + put_dev_sector(sect); 448 } 449 + if (!nr_tbs) { 450 + ldm_crit("Failed to find a valid TOCBLOCK."); 451 + goto err; 452 + } 453 + /* Range check the TOCBLOCK against a privhead. */ 454 if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) || 455 + ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) > 456 + ph->config_size)) { 457 + ldm_crit("The bitmaps are out of range. Giving up."); 458 + goto err; 459 } 460 + /* Compare all loaded TOCBLOCKs. */ 461 + for (i = 1; i < nr_tbs; i++) { 462 + if (!ldm_compare_tocblocks(tb[0], tb[i])) { 463 + ldm_crit("TOCBLOCKs 0 and %d do not match.", i); 464 + goto err; 465 + } 466 } 467 + ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs); 468 result = true; 469 + err: 470 + kfree(tb[1]); 471 return result; 472 } 473 ··· 566 567 p = (struct partition*)(data + 0x01BE); 568 for (i = 0; i < 4; i++, p++) 569 + if (SYS_IND (p) == LDM_PARTITION) { 570 result = true; 571 break; 572 } ··· 975 * Return: 'true' @vb contains a Partition VBLK 976 * 'false' @vb contents are not defined 977 */ 978 + static bool ldm_parse_prt3(const u8 *buffer, int buflen, struct vblk *vb) 979 { 980 int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len; 981 struct vblk_part *part; 982 983 + BUG_ON(!buffer || !vb); 984 + r_objid = ldm_relative(buffer, buflen, 0x18, 0); 985 + if (r_objid < 0) { 986 + ldm_error("r_objid %d < 0", r_objid); 987 + return false; 988 + } 989 + r_name = ldm_relative(buffer, buflen, 0x18, r_objid); 990 + if (r_name < 0) { 991 + ldm_error("r_name %d < 0", r_name); 992 + return false; 993 + } 994 + r_size = ldm_relative(buffer, buflen, 0x34, r_name); 995 + if (r_size < 0) { 996 + ldm_error("r_size %d < 0", r_size); 997 + return false; 998 + } 999 + r_parent = ldm_relative(buffer, buflen, 0x34, r_size); 1000 + if (r_parent < 0) { 1001 + ldm_error("r_parent %d < 0", r_parent); 1002 + return false; 1003 + } 1004 + r_diskid = ldm_relative(buffer, buflen, 0x34, r_parent); 1005 + if (r_diskid < 0) { 1006 + ldm_error("r_diskid %d < 0", r_diskid); 1007 + return false; 1008 + } 1009 if (buffer[0x12] & VBLK_FLAG_PART_INDEX) { 1010 + r_index = ldm_relative(buffer, buflen, 0x34, r_diskid); 1011 + if (r_index < 0) { 1012 + ldm_error("r_index %d < 0", r_index); 1013 + return false; 1014 + } 1015 len = r_index; 1016 } else { 1017 r_index = 0; 1018 len = r_diskid; 1019 } 1020 + if (len < 0) { 1021 + ldm_error("len %d < 0", len); 1022 return false; 1023 + } 1024 len += VBLK_SIZE_PRT3; 1025 + if (len > BE32(buffer + 0x14)) { 1026 + ldm_error("len %d > BE32(buffer + 0x14) %d", len, 1027 + BE32(buffer + 0x14)); 1028 return false; 1029 + } 1030 part = &vb->vblk.part; 1031 + part->start = BE64(buffer + 0x24 + r_name); 1032 + part->volume_offset = BE64(buffer + 0x2C + r_name); 1033 + part->size = ldm_get_vnum(buffer + 0x34 + r_name); 1034 + part->parent_id = ldm_get_vnum(buffer + 0x34 + r_size); 1035 + part->disk_id = ldm_get_vnum(buffer + 0x34 + r_parent); 1036 if (vb->flags & VBLK_FLAG_PART_INDEX) 1037 part->partnum = buffer[0x35 + r_diskid]; 1038 else 1039 part->partnum = 0; 1040 return true; 1041 } 1042 ··· 1475 kfree (ldb); 1476 return result; 1477 }
+3 -3
fs/partitions/ldm.h
··· 2 * ldm - Part of the Linux-NTFS project. 3 * 4 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 5 - * Copyright (C) 2001 Anton Altaparmakov <aia21@cantab.net> 6 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 7 * 8 - * Documentation is available at http://linux-ntfs.sf.net/ldm 9 * 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 ··· 93 94 #define OFF_VMDB 17 /* List of partitions. */ 95 96 - #define WIN2K_DYNAMIC_PARTITION 0x42 /* Formerly SFS (Landis). */ 97 98 #define TOC_BITMAP1 "config" /* Names of the two defined */ 99 #define TOC_BITMAP2 "log" /* bitmaps in the TOCBLOCK. */
··· 2 * ldm - Part of the Linux-NTFS project. 3 * 4 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> 5 + * Copyright (c) 2001-2007 Anton Altaparmakov 6 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> 7 * 8 + * Documentation is available at http://www.linux-ntfs.org/content/view/19/37/ 9 * 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 ··· 93 94 #define OFF_VMDB 17 /* List of partitions. */ 95 96 + #define LDM_PARTITION 0x42 /* Formerly SFS (Landis). */ 97 98 #define TOC_BITMAP1 "config" /* Names of the two defined */ 99 #define TOC_BITMAP2 "log" /* bitmaps in the TOCBLOCK. */