📄 ldm.c
字号:
/** * ldm - Support for Windows Logical Disk Manager (Dynamic Disks) * * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> * Copyright (c) 2001-2004 Anton Altaparmakov * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> * * Documentation is available at http://linux-ntfs.sf.net/ldm * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program (in the main directory of the source in the file COPYING); if * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/stringify.h>#include "ldm.h"#include "check.h"#include "msdos.h"typedef enum { FALSE = 0, TRUE = 1} BOOL;/** * ldm_debug/info/error/crit - Output an error message * @f: A printf format string containing the message * @...: Variables to substitute into @f * * ldm_debug() writes a DEBUG level message to the syslog but only if the * driver was compiled with debug enabled. Otherwise, the call turns into a NOP. */#ifndef CONFIG_LDM_DEBUG#define ldm_debug(...) do {} while (0)#else#define ldm_debug(f, a...) _ldm_printk (KERN_DEBUG, __FUNCTION__, f, ##a)#endif#define ldm_crit(f, a...) _ldm_printk (KERN_CRIT, __FUNCTION__, f, ##a)#define ldm_error(f, a...) _ldm_printk (KERN_ERR, __FUNCTION__, f, ##a)#define ldm_info(f, a...) _ldm_printk (KERN_INFO, __FUNCTION__, f, ##a)__attribute__ ((format (printf, 3, 4)))static void _ldm_printk (const char *level, const char *function, const char *fmt, ...){ static char buf[128]; va_list args; va_start (args, fmt); vsnprintf (buf, sizeof (buf), fmt, args); va_end (args); printk ("%s%s(): %s\n", level, function, buf);}/** * ldm_parse_hexbyte - Convert a ASCII hex number to a byte * @src: Pointer to at least 2 characters to convert. * * Convert a two character ASCII hex string to a number. * * Return: 0-255 Success, the byte was parsed correctly * -1 Error, an invalid character was supplied */static int ldm_parse_hexbyte (const u8 *src){ unsigned int x; /* For correct wrapping */ int h; /* high part */ if ((x = src[0] - '0') <= '9'-'0') h = x; else if ((x = src[0] - 'a') <= 'f'-'a') h = x+10; else if ((x = src[0] - 'A') <= 'F'-'A') h = x+10; else return -1; h <<= 4; /* low part */ if ((x = src[1] - '0') <= '9'-'0') return h | x; if ((x = src[1] - 'a') <= 'f'-'a') return h | (x+10); if ((x = src[1] - 'A') <= 'F'-'A') return h | (x+10); return -1;}/** * ldm_parse_guid - Convert GUID from ASCII to binary * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * @dest: Memory block to hold binary GUID (16 bytes) * * N.B. The GUID need not be NULL terminated. * * Return: TRUE @dest contains binary GUID * FALSE @dest contents are undefined */static BOOL ldm_parse_guid (const u8 *src, u8 *dest){ static const int size[] = { 4, 2, 2, 2, 6 }; int i, j, v; if (src[8] != '-' || src[13] != '-' || src[18] != '-' || src[23] != '-') return FALSE; for (j = 0; j < 5; j++, src++) for (i = 0; i < size[j]; i++, src+=2, *dest++ = v) if ((v = ldm_parse_hexbyte (src)) < 0) return FALSE; return TRUE;}/** * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure * @data: Raw database PRIVHEAD structure loaded from the device * @ph: In-memory privhead structure in which to return parsed information * * This parses the LDM database PRIVHEAD structure supplied in @data and * sets up the in-memory privhead structure @ph with the obtained information. * * Return: TRUE @ph contains the PRIVHEAD data * FALSE @ph contents are undefined */static BOOL ldm_parse_privhead (const u8 *data, struct privhead *ph){ BUG_ON (!data || !ph); if (MAGIC_PRIVHEAD != BE64 (data)) { ldm_error ("Cannot find PRIVHEAD structure. LDM database is" " corrupt. Aborting."); return FALSE; } ph->ver_major = BE16 (data + 0x000C); ph->ver_minor = BE16 (data + 0x000E); ph->logical_disk_start = BE64 (data + 0x011B); ph->logical_disk_size = BE64 (data + 0x0123); ph->config_start = BE64 (data + 0x012B); ph->config_size = BE64 (data + 0x0133); if ((ph->ver_major != 2) || (ph->ver_minor != 11)) { ldm_error ("Expected PRIVHEAD version %d.%d, got %d.%d." " Aborting.", 2, 11, ph->ver_major, ph->ver_minor); return FALSE; } if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */ /* Warn the user and continue, carefully */ ldm_info ("Database is normally %u bytes, it claims to " "be %llu bytes.", LDM_DB_SIZE, (unsigned long long)ph->config_size ); } if ((ph->logical_disk_size == 0) || (ph->logical_disk_start + ph->logical_disk_size > ph->config_start)) { ldm_error ("PRIVHEAD disk size doesn't match real disk size"); return FALSE; } if (!ldm_parse_guid (data + 0x0030, ph->disk_id)) { ldm_error ("PRIVHEAD contains an invalid GUID."); return FALSE; } ldm_debug ("Parsed PRIVHEAD successfully."); return TRUE;}/** * ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure * @data: Raw database TOCBLOCK structure loaded from the device * @toc: In-memory toc structure in which to return parsed information * * This parses the LDM Database TOCBLOCK (table of contents) structure supplied * in @data and sets up the in-memory tocblock structure @toc with the obtained * information. * * N.B. The *_start and *_size values returned in @toc are not range-checked. * * Return: TRUE @toc contains the TOCBLOCK data * FALSE @toc contents are undefined */static BOOL ldm_parse_tocblock (const u8 *data, struct tocblock *toc){ BUG_ON (!data || !toc); if (MAGIC_TOCBLOCK != BE64 (data)) { ldm_crit ("Cannot find TOCBLOCK, database may be corrupt."); return FALSE; } strncpy (toc->bitmap1_name, data + 0x24, sizeof (toc->bitmap1_name)); toc->bitmap1_name[sizeof (toc->bitmap1_name) - 1] = 0; toc->bitmap1_start = BE64 (data + 0x2E); toc->bitmap1_size = BE64 (data + 0x36); if (strncmp (toc->bitmap1_name, TOC_BITMAP1, sizeof (toc->bitmap1_name)) != 0) { ldm_crit ("TOCBLOCK's first bitmap is '%s', should be '%s'.", TOC_BITMAP1, toc->bitmap1_name); return FALSE; } strncpy (toc->bitmap2_name, data + 0x46, sizeof (toc->bitmap2_name)); toc->bitmap2_name[sizeof (toc->bitmap2_name) - 1] = 0; toc->bitmap2_start = BE64 (data + 0x50); toc->bitmap2_size = BE64 (data + 0x58); if (strncmp (toc->bitmap2_name, TOC_BITMAP2, sizeof (toc->bitmap2_name)) != 0) { ldm_crit ("TOCBLOCK's second bitmap is '%s', should be '%s'.", TOC_BITMAP2, toc->bitmap2_name); return FALSE; } ldm_debug ("Parsed TOCBLOCK successfully."); return TRUE;}/** * ldm_parse_vmdb - Read the LDM Database VMDB structure * @data: Raw database VMDB structure loaded from the device * @vm: In-memory vmdb structure in which to return parsed information * * This parses the LDM Database VMDB structure supplied in @data and sets up * the in-memory vmdb structure @vm with the obtained information. * * N.B. The *_start, *_size and *_seq values will be range-checked later. * * Return: TRUE @vm contains VMDB info * FALSE @vm contents are undefined */static BOOL ldm_parse_vmdb (const u8 *data, struct vmdb *vm){ BUG_ON (!data || !vm); if (MAGIC_VMDB != BE32 (data)) { ldm_crit ("Cannot find the VMDB, database may be corrupt."); return FALSE; } vm->ver_major = BE16 (data + 0x12); vm->ver_minor = BE16 (data + 0x14); if ((vm->ver_major != 4) || (vm->ver_minor != 10)) { ldm_error ("Expected VMDB version %d.%d, got %d.%d. " "Aborting.", 4, 10, vm->ver_major, vm->ver_minor); return FALSE; } vm->vblk_size = BE32 (data + 0x08); vm->vblk_offset = BE32 (data + 0x0C); vm->last_vblk_seq = BE32 (data + 0x04); ldm_debug ("Parsed VMDB successfully."); return TRUE;}/** * ldm_compare_privheads - Compare two privhead objects * @ph1: First privhead * @ph2: Second privhead * * This compares the two privhead structures @ph1 and @ph2. * * Return: TRUE Identical * FALSE Different */static BOOL ldm_compare_privheads (const struct privhead *ph1, const struct privhead *ph2){ BUG_ON (!ph1 || !ph2); return ((ph1->ver_major == ph2->ver_major) && (ph1->ver_minor == ph2->ver_minor) && (ph1->logical_disk_start == ph2->logical_disk_start) && (ph1->logical_disk_size == ph2->logical_disk_size) && (ph1->config_start == ph2->config_start) && (ph1->config_size == ph2->config_size) && !memcmp (ph1->disk_id, ph2->disk_id, GUID_SIZE));}/** * ldm_compare_tocblocks - Compare two tocblock objects * @toc1: First toc * @toc2: Second toc * * This compares the two tocblock structures @toc1 and @toc2. * * Return: TRUE Identical * FALSE Different */static BOOL ldm_compare_tocblocks (const struct tocblock *toc1, const struct tocblock *toc2){ BUG_ON (!toc1 || !toc2); return ((toc1->bitmap1_start == toc2->bitmap1_start) && (toc1->bitmap1_size == toc2->bitmap1_size) && (toc1->bitmap2_start == toc2->bitmap2_start) && (toc1->bitmap2_size == toc2->bitmap2_size) && !strncmp (toc1->bitmap1_name, toc2->bitmap1_name, sizeof (toc1->bitmap1_name)) && !strncmp (toc1->bitmap2_name, toc2->bitmap2_name, sizeof (toc1->bitmap2_name)));}/** * ldm_validate_privheads - Compare the primary privhead with its backups * @bdev: Device holding the LDM Database * @ph1: Memory struct to fill with ph contents * * Read and compare all three privheads from disk. * * The privheads on disk show the size and location of the main disk area and * the configuration area (the database). The values are range-checked against * @hd, which contains the real size of the disk. * * Return: TRUE Success * FALSE Error */static BOOL ldm_validate_privheads (struct block_device *bdev, struct privhead *ph1){ static const int off[3] = { OFF_PRIV1, OFF_PRIV2, OFF_PRIV3 }; struct privhead *ph[3] = { ph1 }; Sector sect; u8 *data; BOOL result = FALSE; long num_sects; int i; BUG_ON (!bdev || !ph1); ph[1] = kmalloc (sizeof (*ph[1]), GFP_KERNEL); ph[2] = kmalloc (sizeof (*ph[2]), GFP_KERNEL); if (!ph[1] || !ph[2]) { ldm_crit ("Out of memory."); goto out; } /* off[1 & 2] are relative to ph[0]->config_start */ ph[0]->config_start = 0; /* Read and parse privheads */ for (i = 0; i < 3; i++) { data = read_dev_sector (bdev, ph[0]->config_start + off[i], §); if (!data) { ldm_crit ("Disk read failed."); goto out; } result = ldm_parse_privhead (data, ph[i]); put_dev_sector (sect); if (!result) { ldm_error ("Cannot find PRIVHEAD %d.", i+1); /* Log again */ if (i < 2) goto out; /* Already logged */ else break; /* FIXME ignore for now, 3rd PH can fail on odd-sized disks */ } } num_sects = bdev->bd_inode->i_size >> 9; if ((ph[0]->config_start > num_sects) || ((ph[0]->config_start + ph[0]->config_size) > num_sects)) { ldm_crit ("Database extends beyond the end of the disk."); goto out; } if ((ph[0]->logical_disk_start > ph[0]->config_start) || ((ph[0]->logical_disk_start + ph[0]->logical_disk_size) > ph[0]->config_start)) { ldm_crit ("Disk and database overlap."); goto out; } if (!ldm_compare_privheads (ph[0], ph[1])) { ldm_crit ("Primary and backup PRIVHEADs don't match."); goto out; } /* FIXME ignore this for now if (!ldm_compare_privheads (ph[0], ph[2])) { ldm_crit ("Primary and backup PRIVHEADs don't match."); goto out; }*/ ldm_debug ("Validated PRIVHEADs successfully."); result = TRUE;out: kfree (ph[1]); kfree (ph[2]); return result;}/** * ldm_validate_tocblocks - Validate the table of contents and its backups * @bdev: Device holding the LDM Database * @base: Offset, into @bdev, of the database * @ldb: Cache of the database structures * * Find and compare the four tables of contents of the LDM Database stored on * @bdev and return the parsed information into @toc1. * * The offsets and sizes of the configs are range-checked against a privhead. * * Return: TRUE @toc1 contains validated TOCBLOCK info * FALSE @toc1 contents are undefined */static BOOL ldm_validate_tocblocks (struct block_device *bdev, unsigned long base, struct ldmdb *ldb){ static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4}; struct tocblock *tb[4]; struct privhead *ph; Sector sect; u8 *data; BOOL result = FALSE; int i; BUG_ON (!bdev || !ldb); ph = &ldb->ph; tb[0] = &ldb->toc; tb[1] = kmalloc (sizeof (*tb[1]), GFP_KERNEL); tb[2] = kmalloc (sizeof (*tb[2]), GFP_KERNEL); tb[3] = kmalloc (sizeof (*tb[3]), GFP_KERNEL); if (!tb[1] || !tb[2] || !tb[3]) { ldm_crit ("Out of memory."); goto out; } for (i = 0; i < 4; i++) /* Read and parse all four toc's. */ { data = read_dev_sector (bdev, base + off[i], §); if (!data) { ldm_crit ("Disk read failed."); goto out; } result = ldm_parse_tocblock (data, tb[i]); put_dev_sector (sect); if (!result) goto out; /* Already logged */ } /* Range check the toc against a privhead. */ if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) || ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) > ph->config_size)) { ldm_crit ("The bitmaps are out of range. Giving up."); goto out; } if (!ldm_compare_tocblocks (tb[0], tb[1]) || /* Compare all tocs. */ !ldm_compare_tocblocks (tb[0], tb[2]) || !ldm_compare_tocblocks (tb[0], tb[3])) { ldm_crit ("The TOCBLOCKs don't match."); goto out; } ldm_debug ("Validated TOCBLOCKs successfully."); result = TRUE;out: kfree (tb[1]); kfree (tb[2]); kfree (tb[3]); return result;}/** * ldm_validate_vmdb - Read the VMDB and validate it * @bdev: Device holding the LDM Database * @base: Offset, into @bdev, of the database * @ldb: Cache of the database structures * * Find the vmdb of the LDM Database stored on @bdev and return the parsed * information in @ldb. * * Return: TRUE @ldb contains validated VBDB info * FALSE @ldb contents are undefined */static BOOL ldm_validate_vmdb (struct block_device *bdev, unsigned long base, struct ldmdb *ldb){ Sector sect; u8 *data; BOOL result = FALSE; struct vmdb *vm; struct tocblock *toc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -