⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ldm.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/** * ldm - Support for Windows Logical Disk Manager (Dynamic Disks) * * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org> * Copyright (c) 2001-2007 Anton Altaparmakov * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com> * * Documentation is available at http://www.linux-ntfs.org/content/view/19/37/ * * 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"/** * 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){	bool is_vista = false;	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);	/* Version 2.11 is Win2k/XP and version 2.12 is Vista. */	if (ph->ver_major == 2 && ph->ver_minor == 12)		is_vista = true;	if (!is_vista && (ph->ver_major != 2 || ph->ver_minor != 11)) {		ldm_error("Expected PRIVHEAD version 2.11 or 2.12, got %d.%d."			" Aborting.", ph->ver_major, ph->ver_minor);		return false;	}	ldm_debug("PRIVHEAD version %d.%d (Windows %s).", ph->ver_major,			ph->ver_minor, is_vista ? "Vista" : "2000/XP");	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], &sect);		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;	int i, nr_tbs;	bool result = false;	BUG_ON(!bdev || !ldb);	ph = &ldb->ph;	tb[0] = &ldb->toc;	tb[1] = kmalloc(sizeof(*tb[1]) * 3, GFP_KERNEL);	if (!tb[1]) {		ldm_crit("Out of memory.");		goto err;	}	tb[2] = (struct tocblock*)((u8*)tb[1] + sizeof(*tb[1]));	tb[3] = (struct tocblock*)((u8*)tb[2] + sizeof(*tb[2]));	/*	 * Try to read and parse all four TOCBLOCKs.	 *	 * Windows Vista LDM v2.12 does not always have all four TOCBLOCKs so	 * skip any that fail as long as we get at least one valid TOCBLOCK.	 */	for (nr_tbs = i = 0; i < 4; i++) {		data = read_dev_sector(bdev, base + off[i], &sect);		if (!data) {			ldm_error("Disk read failed for TOCBLOCK %d.", i);			continue;		}		if (ldm_parse_tocblock(data, tb[nr_tbs]))			nr_tbs++;		put_dev_sector(sect);	}	if (!nr_tbs) {		ldm_crit("Failed to find a valid TOCBLOCK.");		goto err;	}	/* Range check the TOCBLOCK 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 err;	}	/* Compare all loaded TOCBLOCKs. */	for (i = 1; i < nr_tbs; i++) {		if (!ldm_compare_tocblocks(tb[0], tb[i])) {			ldm_crit("TOCBLOCKs 0 and %d do not match.", i);			goto err;		}	}	ldm_debug("Validated %d TOCBLOCKs successfully.", nr_tbs);	result = true;err:	kfree(tb[1]);	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;	BUG_ON (!bdev || !ldb);	vm  = &ldb->vm;	toc = &ldb->toc;	data = read_dev_sector (bdev, base + OFF_VMDB, &sect);	if (!data) {		ldm_crit ("Disk read failed.");		return false;	}	if (!ldm_parse_vmdb (data, vm))		goto out;				/* Already logged */	/* Are there uncommitted transactions? */	if (BE16(data + 0x10) != 0x01) {		ldm_crit ("Database is not in a consistent state.  Aborting.");		goto out;	}	if (vm->vblk_offset != 512)		ldm_info ("VBLKs start at offset 0x%04x.", vm->vblk_offset);	/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -