📄 volume.c
字号:
/** * volume.c - NTFS volume handling code. Part of the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov * Copyright (c) 2002-2005 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 Linux-NTFS * distribution in the file COPYING); if not, write to the Free Software * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Modified 01/2007 by Andy McLaughlin for Visopsys port. */#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"#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; if (NDevDirty(dev)) dev->d_ops->sync(dev); if (dev->d_ops->close(dev)) ntfs_log_perror("Eeek! Failed to close the device. Error: "); } if (v->vol_name) free(v->vol_name); if (v->upcase) free(v->upcase); if (v->attrdef) free(v->attrdef); free(v);}/** * 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 = (MFT_RECORD*)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_debug("Error: Incomplete multi sector transfer detected in " "$MFT.\n"); goto io_error_exit; } if (!ntfs_is_mft_record(mb->magic)) { ntfs_log_debug("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_debug("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_debug("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_debug("Error: $MFT/$ATTRIBUTE_LIST has invalid length.\n"); goto io_error_exit; } vol->mft_ni->attr_list_size = l; vol->mft_ni->attr_list = malloc(l); if (!vol->mft_ni->attr_list) { ntfs_log_debug("Error: failed to allocate buffer for attribute " "list.\n"); goto error_exit; } l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); if (!l) { ntfs_log_debug("Error: failed to get value of " "$MFT/$ATTRIBUTE_LIST.\n"); goto io_error_exit; } if (l != vol->mft_ni->attr_list_size) { ntfs_log_debug("Error: got unexpected amount of data when reading " "$MFT/$ATTRIBUTE_LIST.\n"); goto io_error_exit; }mft_has_no_attr_list: /* 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_debug("$MFT must be non-resident but a resident " "extent was found. $MFT is corrupt. Run " "chkdsk.\n"); goto io_error_exit; } /* $MFT must be uncompressed and unencrypted. */ if (a->flags & ATTR_COMPRESSION_MASK || a->flags & ATTR_IS_ENCRYPTED) { ntfs_log_debug("$MFT must be uncompressed and unencrypted " "but a compressed/encrypted extent was " "found. $MFT is corrupt. Run chkdsk.\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_debug("$MFT has corrupt attribute list attribute. " "Run chkdsk.\n"); goto io_error_exit; } } if (!a) { ntfs_log_debug("$MFT/$DATA attribute not found. $MFT is corrupt. Run " "chkdsk.\n"); goto io_error_exit; } if (highest_vcn && highest_vcn != last_vcn - 1) { ntfs_log_debug("Failed to load the complete runlist for $MFT/$DATA. " "Bug or corrupt $MFT. Run chkdsk.\n"); ntfs_log_debug("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 i; runlist_element rl[2]; vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); if (!vol->mftmirr_ni) { ntfs_log_perror("Failed to open inode $MFTMirr"); return -1; } /* Get an ntfs attribute for $MFTMirr/$DATA, too. */ 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; } /* Construct the mft mirror runlist. */ rl[0].vcn = 0; rl[0].lcn = vol->mftmirr_lcn; rl[0].length = (vol->mftmirr_size * vol->mft_record_size + vol->cluster_size - 1) / vol->cluster_size; rl[1].vcn = rl[0].length; rl[1].lcn = LCN_ENOENT; rl[1].length = 0; /* Compare the two runlists. They must be identical. */ i = 0; do { if (rl[i].vcn != vol->mftmirr_na->rl[i].vcn || rl[i].lcn != vol->mftmirr_na->rl[i].lcn || rl[i].length != vol->mftmirr_na->rl[i].length) { ntfs_log_debug("Error: $MFTMirr location mismatch! Run " "chkdsk.\n"); errno = EIO; goto error_exit; } } while (rl[i++].length); return 0;error_exit: i = 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 = i; 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;#ifndef NTFS_DISABLE_DEBUG_LOGGING const char *OK = "OK\n"; const char *FAILED = "FAILED\n"; BOOL debug = 1;#else BOOL debug = 0;#endif if (!dev || !dev->d_ops || !dev->d_name) { errno = EINVAL; return NULL; } /* Allocate the boot sector structure. */ if (!(bs = (NTFS_BOOT_SECTOR *)malloc(sizeof(NTFS_BOOT_SECTOR)))) 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 = (ntfschar*)malloc(vol->upcase_len * sizeof(ntfschar)); if (!vol->upcase) { ntfs_log_perror("Error allocating memory for upcase table."); 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_debug("Error: partition is smaller than bootsector " "size. Weird!\n"); else ntfs_log_perror("Error reading bootsector"); goto error_exit; } ntfs_log_debug(OK); if (!ntfs_boot_sector_is_ntfs(bs, !debug)) { ntfs_log_debug("Error: %s is not a valid NTFS partition!\n", dev->d_name); errno = EINVAL; goto error_exit; } if (ntfs_boot_sector_parse(vol, bs) < 0) { ntfs_log_perror("Failed to parse ntfs bootsector"); 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: "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -