📄 mft.c
字号:
/** * mft.c - Mft record handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy * * 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_ERRNO_H#include <errno.h>#endif#ifdef HAVE_STRING_H#include <string.h>#endif#include <time.h>#include "compat.h"#include "types.h"#include "device.h"#include "debug.h"#include "bitmap.h"#include "attrib.h"#include "inode.h"#include "volume.h"#include "layout.h"#include "lcnalloc.h"#include "mft.h"#include "logging.h"#include "misc.h"/** * ntfs_mft_records_read - read records from the mft from disk * @vol: volume to read from * @mref: starting mft record number to read * @count: number of mft records to read * @b: output data buffer * * Read @count mft records starting at @mref from volume @vol into buffer * @b. Return 0 on success or -1 on error, with errno set to the error * code. * * If any of the records exceed the initialized size of the $MFT/$DATA * attribute, i.e. they cannot possibly be allocated mft records, assume this * is a bug and return error code ESPIPE. * * The read mft records are mst deprotected and are hence ready to use. The * caller should check each record with is_baad_record() in case mst * deprotection failed. * * NOTE: @b has to be at least of size @count * vol->mft_record_size. */int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref, const s64 count, MFT_RECORD *b){ s64 br; VCN m; ntfs_log_trace("inode %llu\n", (unsigned long long)MREF(mref)); if (!vol || !vol->mft_na || !b || count < 0) { errno = EINVAL; ntfs_log_perror("%s: b=%p count=%lld mft=%llu", __FUNCTION__, b, (long long)count, (unsigned long long)MREF(mref)); return -1; } m = MREF(mref); /* Refuse to read non-allocated mft records. */ if (m + count > vol->mft_na->initialized_size >> vol->mft_record_size_bits) { errno = ESPIPE; ntfs_log_perror("Trying to read non-allocated mft records " "(%lld > %lld)", (long long)m + count, (long long)vol->mft_na->initialized_size >> vol->mft_record_size_bits); return -1; } br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits, count, vol->mft_record_size, b); if (br != count) { if (br != -1) errno = EIO; if (br >= 0) ntfs_log_debug("Error: partition is smaller than it should " "be!\n"); else ntfs_log_perror("Error reading $Mft record(s)"); return -1; } return 0;}/** * ntfs_mft_records_write - write mft records to disk * @vol: volume to write to * @mref: starting mft record number to write * @count: number of mft records to write * @b: data buffer containing the mft records to write * * Write @count mft records starting at @mref from data buffer @b to volume * @vol. Return 0 on success or -1 on error, with errno set to the error code. * * If any of the records exceed the initialized size of the $MFT/$DATA * attribute, i.e. they cannot possibly be allocated mft records, assume this * is a bug and return error code ESPIPE. * * Before the mft records are written, they are mst protected. After the write, * they are deprotected again, thus resulting in an increase in the update * sequence number inside the data buffer @b. * * If any mft records are written which are also represented in the mft mirror * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a * temporary buffer before we do the actual write. Then if at least one mft * record was successfully written, we write the appropriate mft records from * the copied buffer to the mft mirror, too. */int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref, const s64 count, MFT_RECORD *b){ s64 bw; VCN m; void *bmirr = NULL; int cnt = 0, res = 0; if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) { errno = EINVAL; return -1; } m = MREF(mref); /* Refuse to write non-allocated mft records. */ if (m + count > vol->mft_na->initialized_size >> vol->mft_record_size_bits) { errno = ESPIPE; ntfs_log_perror("Trying to write non-allocated mft records " "(%lld > %lld)", (long long)m + count, (long long)vol->mft_na->initialized_size >> vol->mft_record_size_bits); return -1; } if (m < vol->mftmirr_size) { if (!vol->mftmirr_na) { errno = EINVAL; return -1; } cnt = vol->mftmirr_size - m; if (cnt > count) cnt = count; bmirr = ntfs_malloc(cnt * vol->mft_record_size); if (!bmirr) return -1; memcpy(bmirr, b, cnt * vol->mft_record_size); } bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits, count, vol->mft_record_size, b); if (bw != count) { if (bw != -1) errno = EIO; if (bw >= 0) ntfs_log_debug("Error: partial write while writing $Mft " "record(s)!\n"); else ntfs_log_perror("Error writing $Mft record(s)"); res = errno; } if (bmirr && bw > 0) { if (bw < cnt) cnt = bw; bw = ntfs_attr_mst_pwrite(vol->mftmirr_na, m << vol->mft_record_size_bits, cnt, vol->mft_record_size, bmirr); if (bw != cnt) { if (bw != -1) errno = EIO; ntfs_log_debug("Error: failed to sync $MFTMirr! Run " "chkdsk.\n"); res = errno; } } free(bmirr); if (!res) return res; errno = res; return -1;}int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *m){ ATTR_RECORD *a; int ret = -1; if (!ntfs_is_file_record(m->magic)) { ntfs_log_error("Record %llu has no FILE magic (0x%x)\n", (unsigned long long)MREF(mref), *(le32 *)m); goto err_out; } if (le32_to_cpu(m->bytes_allocated) != vol->mft_record_size) { ntfs_log_error("Record %llu has corrupt allocation size " "(%u <> %u)\n", (unsigned long long)MREF(mref), vol->mft_record_size, le32_to_cpu(m->bytes_allocated)); goto err_out; } a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset)); if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) { ntfs_log_error("Record %llu is corrupt\n", (unsigned long long)MREF(mref)); goto err_out; } ret = 0;err_out: if (ret) errno = EIO; return ret;}/** * ntfs_file_record_read - read a FILE record from the mft from disk * @vol: volume to read from * @mref: mft reference specifying mft record to read * @mrec: address of pointer in which to return the mft record * @attr: address of pointer in which to return the first attribute * * Read a FILE record from the mft of @vol from the storage medium. @mref * specifies the mft record to read, including the sequence number, which can * be 0 if no sequence number checking is to be performed. * * The function allocates a buffer large enough to hold the mft record and * reads the record into the buffer (mst deprotecting it in the process). * *@mrec is then set to point to the buffer. * * If @attr is not NULL, *@attr is set to point to the first attribute in the * mft record, i.e. *@attr is a pointer into *@mrec. * * Return 0 on success, or -1 on error, with errno set to the error code. * * The read mft record is checked for having the magic FILE, * and for having a matching sequence number (if MSEQNO(*@mref) != 0). * If either of these fails, -1 is returned and errno is set to EIO. If you get * this, but you still want to read the mft record (e.g. in order to correct * it), use ntfs_mft_record_read() directly. * * Note: Caller has to free *@mrec when finished. * * Note: We do not check if the mft record is flagged in use. The caller can * check if desired. */int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD **mrec, ATTR_RECORD **attr){ MFT_RECORD *m; if (!vol || !mrec) { errno = EINVAL; ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); return -1; } m = *mrec; if (!m) { m = ntfs_malloc(vol->mft_record_size); if (!m) return -1; } if (ntfs_mft_record_read(vol, mref, m)) { ntfs_log_perror("ntfs_mft_record_read failed"); goto err_out; } if (ntfs_mft_record_check(vol, mref, m)) goto err_out; if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number)) { ntfs_log_error("Record %llu has wrong SeqNo (%d <> %d)\n", (unsigned long long)MREF(mref), MSEQNO(mref), le16_to_cpu(m->sequence_number)); errno = EIO; goto err_out; } *mrec = m; if (attr) *attr = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); return 0;err_out: if (m != *mrec) free(m); return -1;}/** * ntfs_mft_record_layout - layout an mft record into a memory buffer * @vol: volume to which the mft record will belong * @mref: mft reference specifying the mft record number * @mrec: destination buffer of size >= @vol->mft_record_size bytes * * Layout an empty, unused mft record with the mft reference @mref into the * buffer @m. The volume @vol is needed because the mft record structure was * modified in NTFS 3.1 so we need to know which volume version this mft record * will be used on. * * On success return 0 and on error return -1 with errno set to the error code. */int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref, MFT_RECORD *mrec){ ATTR_RECORD *a; if (!vol || !mrec) { errno = EINVAL; ntfs_log_perror("%s: mrec=%p", __FUNCTION__, mrec); return -1; } /* Aligned to 2-byte boundary. */ if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver)) mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1); else { /* Abort if mref is > 32 bits. */ if (MREF(mref) & 0x0000ffff00000000ull) { errno = ERANGE; ntfs_log_perror("Mft reference exceeds 32 bits"); return -1; } mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1); /* * Set the NTFS 3.1+ specific fields while we know that the * volume version is 3.1+. */ mrec->reserved = cpu_to_le16(0); mrec->mft_record_number = cpu_to_le32(MREF(mref)); } mrec->magic = magic_FILE; if (vol->mft_record_size >= NTFS_BLOCK_SIZE) mrec->usa_count = cpu_to_le16(vol->mft_record_size / NTFS_BLOCK_SIZE + 1); else { mrec->usa_count = cpu_to_le16(1); ntfs_log_error("Sector size is bigger than MFT record size. " "Setting usa_count to 1. If Windows chkdsk " "reports this as corruption, please email %s " "stating that you saw this message and that " "the file system created was corrupt. " "Thank you.\n", NTFS_DEV_LIST); } /* Set the update sequence number to 1. */ *(u16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1); mrec->lsn = cpu_to_le64(0ull); mrec->sequence_number = cpu_to_le16(1); mrec->link_count = cpu_to_le16(0); /* Aligned to 8-byte boundary. */ mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) + (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7); mrec->flags = cpu_to_le16(0); /* * Using attrs_offset plus eight bytes (for the termination attribute), * aligned to 8-byte boundary. */ mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 + 7) & ~7); mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size); mrec->base_mft_record = cpu_to_le64((MFT_REF)0); mrec->next_attr_instance = cpu_to_le16(0); a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); a->type = AT_END; a->length = cpu_to_le32(0); /* Finally, clear the unused part of the mft record. */ memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec)); return 0;}/** * ntfs_mft_record_format - format an mft record on an ntfs volume * @vol: volume on which to format the mft record * @mref: mft reference specifying mft record to format * * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay * out an empty, unused mft record in memory and write it to the volume @vol. * * On success return 0 and on error return -1 with errno set to the error code.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -