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