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

📄 volume.c

📁 添加linux下对NTFS格式文件系统访问支持的源代码ntfs-3g
💻 C
📖 第 1 页 / 共 3 页
字号:
/** * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov * Copyright (c) 2002-2006 Szabolcs Szakacsits * Copyright (c) 2004-2005 Richard Russon * * This program/include file 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/include file 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 NTFS-3G * distribution in the file COPYING); if not, write to the Free Software * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#ifdef HAVE_CONFIG_H#include "config.h"#endif#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_STDIO_H#include <stdio.h>#endif#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_FCNTL_H#include <fcntl.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef HAVE_ERRNO_H#include <errno.h>#endif#ifdef HAVE_SYS_STAT_H#include <sys/stat.h>#endif#ifdef HAVE_LIMITS_H#include <limits.h>#endif#include "volume.h"#include "attrib.h"#include "mft.h"#include "bootsect.h"#include "device.h"#include "debug.h"#include "inode.h"#include "runlist.h"#include "logfile.h"#include "dir.h"#include "logging.h"#include "misc.h"#ifndef PATH_MAX#define PATH_MAX 4096#endif/** * ntfs_volume_alloc - Create an NTFS volume object and initialise it * * Description... * * Returns: */ntfs_volume *ntfs_volume_alloc(void){	return calloc(1, sizeof(ntfs_volume));}/** * __ntfs_volume_release - Destroy an NTFS volume object * @v: * * Description... * * Returns: */static void __ntfs_volume_release(ntfs_volume *v){	if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni))		ntfs_inode_sync(v->lcnbmp_ni);	if (v->vol_ni)		ntfs_inode_close(v->vol_ni);	if (v->lcnbmp_na)		ntfs_attr_close(v->lcnbmp_na);	if (v->lcnbmp_ni)		ntfs_inode_close(v->lcnbmp_ni);	if (v->mft_ni && NInoDirty(v->mft_ni))		ntfs_inode_sync(v->mft_ni);	if (v->mftbmp_na)		ntfs_attr_close(v->mftbmp_na);	if (v->mft_na)		ntfs_attr_close(v->mft_na);	if (v->mft_ni)		ntfs_inode_close(v->mft_ni);	if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni))		ntfs_inode_sync(v->mftmirr_ni);	if (v->mftmirr_na)		ntfs_attr_close(v->mftmirr_na);	if (v->mftmirr_ni)		ntfs_inode_close(v->mftmirr_ni);	if (v->dev) {		struct ntfs_device *dev = v->dev;		dev->d_ops->sync(dev);		if (dev->d_ops->close(dev))			ntfs_log_perror("Failed to close the device");	}	free(v->vol_name);	free(v->upcase);	free(v->attrdef);	free(v);}static void ntfs_attr_setup_flag(ntfs_inode *ni){	STANDARD_INFORMATION *si;	si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL);	if (si) {		ni->flags = si->file_attributes;		free(si);	}}/** * ntfs_mft_load - load the $MFT and setup the ntfs volume with it * @vol:	ntfs volume whose $MFT to load * * Load $MFT from @vol and setup @vol with it. After calling this function the * volume @vol is ready for use by all read access functions provided by the * ntfs library. * * Return 0 on success and -1 on error with errno set to the error code. */static int ntfs_mft_load(ntfs_volume *vol){	VCN next_vcn, last_vcn, highest_vcn;	s64 l;	MFT_RECORD *mb = NULL;	ntfs_attr_search_ctx *ctx = NULL;	ATTR_RECORD *a;	int eo;	/* Manually setup an ntfs_inode. */	vol->mft_ni = ntfs_inode_allocate(vol);	mb = ntfs_malloc(vol->mft_record_size);	if (!vol->mft_ni || !mb) {		ntfs_log_perror("Error allocating memory for $MFT");		goto error_exit;	}	vol->mft_ni->mft_no = 0;	vol->mft_ni->mrec = mb;	/* Can't use any of the higher level functions yet! */	l = ntfs_mst_pread(vol->dev, vol->mft_lcn << vol->cluster_size_bits, 1,			vol->mft_record_size, mb);	if (l != 1) {		if (l != -1)			errno = EIO;		ntfs_log_perror("Error reading $MFT");		goto error_exit;	}	if (ntfs_is_baad_record(mb->magic)) {		ntfs_log_error("Incomplete multi sector transfer detected in "				"$MFT.\n");		goto io_error_exit;	}	if (!ntfs_is_mft_record(mb->magic)) {		ntfs_log_error("$MFT has invalid magic.\n");		goto io_error_exit;	}	ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL);	if (!ctx) {		ntfs_log_perror("Failed to allocate attribute search context");		goto error_exit;	}	if (p2n(ctx->attr) < p2n(mb) ||			(char*)ctx->attr > (char*)mb + vol->mft_record_size) {		ntfs_log_error("$MFT is corrupt.\n");		goto io_error_exit;	}	/* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */	if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,			ctx)) {		if (errno != ENOENT) {			ntfs_log_error("$MFT has corrupt attribute list.\n");			goto io_error_exit;		}		goto mft_has_no_attr_list;	}	NInoSetAttrList(vol->mft_ni);	l = ntfs_get_attribute_value_length(ctx->attr);	if (l <= 0 || l > 0x40000) {		ntfs_log_error("$MFT/$ATTR_LIST invalid length (%lld).\n",			       (long long)l);		goto io_error_exit;	}	vol->mft_ni->attr_list_size = l;	vol->mft_ni->attr_list = ntfs_malloc(l);	if (!vol->mft_ni->attr_list)		goto error_exit;		l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list);	if (!l) {		ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n");		goto io_error_exit;	}	if (l != vol->mft_ni->attr_list_size) {		ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != "			       "%u).\n", (long long)l,			       vol->mft_ni->attr_list_size);		goto io_error_exit;	}mft_has_no_attr_list:	ntfs_attr_setup_flag(vol->mft_ni);		/* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */		/* Get an ntfs attribute for $MFT/$DATA and set it up, too. */	vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0);	if (!vol->mft_na) {		ntfs_log_perror("Failed to open ntfs attribute");		goto error_exit;	}	/* Read all extents from the $DATA attribute in $MFT. */	ntfs_attr_reinit_search_ctx(ctx);	last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits;	highest_vcn = next_vcn = 0;	a = NULL;	while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0,			ctx)) {		runlist_element *nrl;		a = ctx->attr;		/* $MFT must be non-resident. */		if (!a->non_resident) {			ntfs_log_error("$MFT must be non-resident.\n");			goto io_error_exit;		}		/* $MFT must be uncompressed and unencrypted. */		if (a->flags & ATTR_COMPRESSION_MASK ||				a->flags & ATTR_IS_ENCRYPTED) {			ntfs_log_error("$MFT must be uncompressed and "				       "unencrypted.\n");			goto io_error_exit;		}		/*		 * Decompress the mapping pairs array of this extent and merge		 * the result into the existing runlist. No need for locking		 * as we have exclusive access to the inode at this time and we		 * are a mount in progress task, too.		 */		nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl);		if (!nrl) {			ntfs_log_perror("ntfs_mapping_pairs_decompress() failed");			goto error_exit;		}		vol->mft_na->rl = nrl;		/* Get the lowest vcn for the next extent. */		highest_vcn = sle64_to_cpu(a->highest_vcn);		next_vcn = highest_vcn + 1;		/* Only one extent or error, which we catch below. */		if (next_vcn <= 0)			break;		/* Avoid endless loops due to corruption. */		if (next_vcn < sle64_to_cpu(a->lowest_vcn)) {			ntfs_log_error("$MFT has corrupt attribute list.\n");			goto io_error_exit;		}	}	if (!a) {		ntfs_log_error("$MFT/$DATA attribute not found.\n");		goto io_error_exit;	}	if (highest_vcn && highest_vcn != last_vcn - 1) {		ntfs_log_error("Failed to load runlist for $MFT/$DATA.\n");		ntfs_log_error("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n",			       (long long)highest_vcn, (long long)last_vcn - 1);		goto io_error_exit;	}	/* Done with the $Mft mft record. */	ntfs_attr_put_search_ctx(ctx);	ctx = NULL;	/*	 * The volume is now setup so we can use all read access functions.	 */	vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0);	if (!vol->mftbmp_na) {		ntfs_log_perror("Failed to open $MFT/$BITMAP");		goto error_exit;	}	return 0;io_error_exit:	errno = EIO;error_exit:	eo = errno;	if (ctx)		ntfs_attr_put_search_ctx(ctx);	if (vol->mft_na) {		ntfs_attr_close(vol->mft_na);		vol->mft_na = NULL;	}	if (vol->mft_ni) {		ntfs_inode_close(vol->mft_ni);		vol->mft_ni = NULL;	}	errno = eo;	return -1;}/** * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it * @vol:	ntfs volume whose $MFTMirr to load * * Load $MFTMirr from @vol and setup @vol with it. After calling this function * the volume @vol is ready for use by all write access functions provided by * the ntfs library (assuming ntfs_mft_load() has been called successfully * beforehand). * * Return 0 on success and -1 on error with errno set to the error code. */static int ntfs_mftmirr_load(ntfs_volume *vol){	int err;	vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr);	if (!vol->mftmirr_ni) {		ntfs_log_perror("Failed to open inode $MFTMirr");		return -1;	}		vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0);	if (!vol->mftmirr_na) {		ntfs_log_perror("Failed to open $MFTMirr/$DATA");		goto error_exit;	}		if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) {		ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA");		goto error_exit;	}		return 0;error_exit:	err = errno;	if (vol->mftmirr_na) {		ntfs_attr_close(vol->mftmirr_na);		vol->mftmirr_na = NULL;	}	ntfs_inode_close(vol->mftmirr_ni);	vol->mftmirr_ni = NULL;	errno = err;	return -1;}/** * ntfs_volume_startup - allocate and setup an ntfs volume * @dev:	device to open * @flags:	optional mount flags * * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After * calling this function, the volume is setup sufficiently to call all read * and write access functions provided by the library. * * Return the allocated volume structure on success and NULL on error with * errno set to the error code. */ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags){	LCN mft_zone_size, mft_lcn;	s64 br;	ntfs_volume *vol;	NTFS_BOOT_SECTOR *bs;	int eo;#ifdef DEBUG	const char *OK = "OK\n";	const char *FAILED = "FAILED\n";#endif	if (!dev || !dev->d_ops || !dev->d_name) {		errno = EINVAL;		return NULL;	}	bs = ntfs_malloc(sizeof(NTFS_BOOT_SECTOR));	if (!bs)		return NULL;		/* Allocate the volume structure. */	vol = ntfs_volume_alloc();	if (!vol)		goto error_exit;	/* Create the default upcase table. */	vol->upcase_len = 65536;	vol->upcase = ntfs_malloc(vol->upcase_len * sizeof(ntfschar));	if (!vol->upcase)		goto error_exit;		ntfs_upcase_table_build(vol->upcase,			vol->upcase_len * sizeof(ntfschar));	if (flags & MS_RDONLY)		NVolSetReadOnly(vol);	if (flags & MS_NOATIME)		NVolSetNoATime(vol);	ntfs_log_debug("Reading bootsector... ");	if (dev->d_ops->open(dev, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) {		ntfs_log_debug(FAILED);		ntfs_log_perror("Error opening partition device");		goto error_exit;	}	/* Attach the device to the volume. */	vol->dev = dev;	/* Now read the bootsector. */	br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs);	if (br != sizeof(NTFS_BOOT_SECTOR)) {		ntfs_log_debug(FAILED);		if (br != -1)			errno = EINVAL;		if (!br)			ntfs_log_error("Partition is smaller than bootsector "				       "size.\n");		else			ntfs_log_perror("Error reading bootsector");		goto error_exit;	}	ntfs_log_debug(OK);	if (!ntfs_boot_sector_is_ntfs(bs)) {		errno = EINVAL;		goto error_exit;	}	if (ntfs_boot_sector_parse(vol, bs) < 0)		goto error_exit;		free(bs);	bs = NULL;	/* Now set the device block size to the sector size. */	if (ntfs_device_block_size_set(vol->dev, vol->sector_size))		ntfs_log_debug("Failed to set the device block size to the "				"sector size.  This may affect performance "				"but should be harmless otherwise.  Error: "				"%s\n", strerror(errno));		/* We now initialize the cluster allocator. */	mft_zone_size = min(vol->nr_clusters >> 3,      /* 12.5% */			    200 * 1000 * 1024 >> vol->cluster_size_bits);	/* Setup the mft zone. */	vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn;	ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)vol->mft_zone_pos);	/*	 * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs	 * source) and if the actual mft_lcn is in the expected place or even	 * further to the front of the volume, extend the mft_zone to cover the	 * beginning of the volume as well. This is in order to protect the	 * area reserved for the mft bitmap as well within the mft_zone itself.	 * On non-standard volumes we don't protect it as the overhead would be	 * higher than the speed increase we would get by doing it.	 */	mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;	if (mft_lcn * vol->cluster_size < 16 * 1024)		mft_lcn = (16 * 1024 + vol->cluster_size - 1) /				vol->cluster_size;	if (vol->mft_zone_start <= mft_lcn)		vol->mft_zone_start = 0;	ntfs_log_debug("mft_zone_start = 0x%llx\n", (long long)vol->mft_zone_start);	/*	 * Need to cap the mft zone on non-standard volumes so that it does	 * not point outside the boundaries of the volume. We do this by	 * halving the zone size until we are inside the volume.	 */	vol->mft_zone_end = vol->mft_lcn + mft_zone_size;	while (vol->mft_zone_end >= vol->nr_clusters) {		mft_zone_size >>= 1;		vol->mft_zone_end = vol->mft_lcn + mft_zone_size;	}	ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end);	/*

⌨️ 快捷键说明

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