📄 dir.c
字号:
/** * dir.c - Directory handling code. Part of the Linux-NTFS project. * * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2005-2006 Yura Pakhuchiy * 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_ERRNO_H#include <errno.h>#endif#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_SYS_STAT_H#include <sys/stat.h>#endif#ifdef HAVE_SYS_SYSMACROS_H#include <sys/sysmacros.h>#endif#include "types.h"#include "debug.h"#include "attrib.h"#include "inode.h"#include "dir.h"#include "volume.h"#include "mft.h"#include "index.h"#include "ntfstime.h"#include "lcnalloc.h"#include "logging.h"/* * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" * and "$Q" as global constants. */ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'), const_cpu_to_le16('3'), const_cpu_to_le16('0'), const_cpu_to_le16('\0') };ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), const_cpu_to_le16('I'), const_cpu_to_le16('I'), const_cpu_to_le16('\0') };ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), const_cpu_to_le16('D'), const_cpu_to_le16('H'), const_cpu_to_le16('\0') };ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'), const_cpu_to_le16('\0') };ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'), const_cpu_to_le16('\0') };ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), const_cpu_to_le16('\0') };/** * ntfs_inode_lookup_by_name - find an inode in a directory given its name * @dir_ni: ntfs inode of the directory in which to search for the name * @uname: Unicode name for which to search in the directory * @uname_len: length of the name @uname in Unicode characters * * Look for an inode with name @uname in the directory with inode @dir_ni. * ntfs_inode_lookup_by_name() walks the contents of the directory looking for * the Unicode name. If the name is found in the directory, the corresponding * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it * is a 64-bit number containing the sequence number. * * On error, return -1 with errno set to the error code. If the inode is is not * found errno is ENOENT. * * Note, @uname_len does not include the (optional) terminating NULL character. * * Note, we look for a case sensitive match first but we also look for a case * insensitive match at the same time. If we find a case insensitive match, we * save that for the case that we don't find an exact match, where we return * the mft reference of the case insensitive match. * * If the volume is mounted with the case sensitive flag set, then we only * allow exact matches. */u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, const int uname_len){ VCN vcn; u64 mref = 0; s64 br; ntfs_volume *vol = dir_ni->vol; ntfs_attr_search_ctx *ctx; INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_ALLOCATION *ia; u8 *index_end; ntfs_attr *ia_na; int eo, rc; u32 index_block_size, index_vcn_size; u8 index_vcn_size_bits; if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) { errno = EINVAL; return -1; } ctx = ntfs_attr_get_search_ctx(dir_ni, NULL); if (!ctx) return -1; /* Find the index root attribute in the mft record. */ if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Index root attribute missing in directory inode " "0x%llx", (unsigned long long)dir_ni->mft_no); goto put_err_out; } /* Get to the index root value. */ ir = (INDEX_ROOT*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); index_block_size = le32_to_cpu(ir->index_block_size); if (index_block_size < NTFS_BLOCK_SIZE || index_block_size & (index_block_size - 1)) { ntfs_log_debug("Index block size %u is invalid.\n", (unsigned)index_block_size); goto put_err_out; } index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length); /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ir->index + le32_to_cpu(ir->index.entries_offset)); /* * Loop until we exceed valid memory (corruption case) or until we * reach the last entry. */ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { /* Bounds checks. */ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > index_end) goto put_err_out; /* * The last entry cannot contain a name. It can however contain * a pointer to a child node in the B+tree so we just break out. */ if (ie->flags & INDEX_ENTRY_END) break; /* * We perform a case sensitive comparison and if that matches * we are done and return the mft reference of the inode (i.e. * the inode number together with the sequence number for * consistency checking). We convert it to cpu format before * returning. */ if (ntfs_names_are_equal(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {found_it: /* * We have a perfect match, so we don't need to care * about having matched imperfectly before. */ mref = le64_to_cpu(ie->indexed_file); ntfs_attr_put_search_ctx(ctx); return mref; } /* * For a case insensitive mount, we also perform a case * insensitive comparison (provided the file name is not in the * POSIX namespace). If the comparison matches, we cache the * mft reference in mref. */ if (!NVolCaseSensitive(vol) && ie->key.file_name.file_name_type && ntfs_names_are_equal(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, IGNORE_CASE, vol->upcase, vol->upcase_len)) { /* Only one case insensitive matching name allowed. */ if (mref) { ntfs_log_error("Found already cached mft " "reference in phase 1. Please " "run chkdsk and if that doesn't" " find any errors please report" " you saw this message to %s\n", NTFS_DEV_LIST); goto put_err_out; } mref = le64_to_cpu(ie->indexed_file); } /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ rc = ntfs_names_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, 1, IGNORE_CASE, vol->upcase, vol->upcase_len); /* * If uname collates before the name of the current entry, there * is definitely no such name in this index but we might need to * descend into the B+tree so we just break out of the loop. */ if (rc == -1) break; /* The names are not equal, continue the search. */ if (rc) continue; /* * Names match with case insensitive comparison, now try the * case sensitive comparison, which is required for proper * collation. */ rc = ntfs_names_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, 1, CASE_SENSITIVE, vol->upcase, vol->upcase_len); if (rc == -1) break; if (rc) continue; /* * Perfect match, this will never happen as the * ntfs_are_names_equal() call will have gotten a match but we * still treat it correctly. */ goto found_it; } /* * We have finished with this index without success. Check for the * presence of a child node and if not present return error code * ENOENT, unless we have got the mft reference of a matching name * cached in mref in which case return mref. */ if (!(ie->flags & INDEX_ENTRY_NODE)) { ntfs_attr_put_search_ctx(ctx); if (mref) return mref; ntfs_log_debug("Entry not found.\n"); errno = ENOENT; return -1; } /* Child node present, descend into it. */ /* Open the index allocation attribute. */ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4); if (!ia_na) { ntfs_log_perror("Failed to open index allocation attribute. Directory " "inode 0x%llx is corrupt or driver bug", (unsigned long long)dir_ni->mft_no); goto put_err_out; } /* Allocate a buffer for the current index block. */ ia = (INDEX_ALLOCATION*)malloc(index_block_size); if (!ia) { ntfs_log_perror("Failed to allocate buffer for index block"); ntfs_attr_close(ia_na); goto put_err_out; } /* Determine the size of a vcn in the directory index. */ if (vol->cluster_size <= index_block_size) { index_vcn_size = vol->cluster_size; index_vcn_size_bits = vol->cluster_size_bits; } else { index_vcn_size = vol->sector_size; index_vcn_size_bits = vol->sector_size_bits; } /* Get the starting vcn of the index_block holding the child node. */ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);descend_into_child_node: /* Read the index block starting at vcn. */ br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1, index_block_size, ia); if (br != 1) { if (br != -1) errno = EIO; ntfs_log_perror("Failed to read vcn 0x%llx", vcn); goto close_err_out; } if (sle64_to_cpu(ia->index_block_vcn) != vcn) { ntfs_log_debug("Actual VCN (0x%llx) of index buffer is different " "from expected VCN (0x%llx).\n", (long long)sle64_to_cpu(ia->index_block_vcn), (long long)vcn); errno = EIO; goto close_err_out; } if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) { ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode 0x%llx " "has a size (%u) differing from the directory " "specified size (%u).\n", (long long)vcn, (unsigned long long)dir_ni->mft_no, (unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18, (unsigned)index_block_size); errno = EIO; goto close_err_out; } index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); if (index_end > (u8*)ia + index_block_size) { ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory inode " "0x%llx exceeds maximum size.\n", (long long)vcn, (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } /* The first index entry. */ ie = (INDEX_ENTRY*)((u8*)&ia->index + le32_to_cpu(ia->index.entries_offset)); /* * Iterate similar to above big loop but applied to index buffer, thus * loop until we exceed valid memory (corruption case) or until we * reach the last entry. */ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) { /* Bounds check. */ if ((u8*)ie < (u8*)ia || (u8*)ie + sizeof(INDEX_ENTRY_HEADER) > index_end || (u8*)ie + le16_to_cpu(ie->key_length) > index_end) { ntfs_log_debug("Index entry out of bounds in directory " "inode 0x%llx.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } /* * The last entry cannot contain a name. It can however contain * a pointer to a child node in the B+tree so we just break out. */ if (ie->flags & INDEX_ENTRY_END) break; /* * We perform a case sensitive comparison and if that matches * we are done and return the mft reference of the inode (i.e. * the inode number together with the sequence number for * consistency checking). We convert it to cpu format before * returning. */ if (ntfs_names_are_equal(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {found_it2: /* * We have a perfect match, so we don't need to care * about having matched imperfectly before. */ mref = le64_to_cpu(ie->indexed_file); free(ia); ntfs_attr_close(ia_na); ntfs_attr_put_search_ctx(ctx); return mref; } /* * For a case insensitive mount, we also perform a case * insensitive comparison (provided the file name is not in the * POSIX namespace). If the comparison matches, we cache the * mft reference in mref. */ if (!NVolCaseSensitive(vol) && ie->key.file_name.file_name_type && ntfs_names_are_equal(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, IGNORE_CASE, vol->upcase, vol->upcase_len)) { /* Only one case insensitive matching name allowed. */ if (mref) { ntfs_log_error("Found already cached mft " "reference in phase 2. Please " "run chkdsk and if that doesn't" " find any errors please report" " you saw this message to %s\n", NTFS_DEV_LIST); goto close_err_out; } mref = le64_to_cpu(ie->indexed_file); } /* * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ rc = ntfs_names_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, 1, IGNORE_CASE, vol->upcase, vol->upcase_len); /* * If uname collates before the name of the current entry, there * is definitely no such name in this index but we might need to * descend into the B+tree so we just break out of the loop. */ if (rc == -1) break; /* The names are not equal, continue the search. */ if (rc) continue; /* * Names match with case insensitive comparison, now try the * case sensitive comparison, which is required for proper * collation. */ rc = ntfs_names_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, ie->key.file_name.file_name_length, 1, CASE_SENSITIVE, vol->upcase, vol->upcase_len); if (rc == -1) break; if (rc) continue; /* * Perfect match, this will never happen as the * ntfs_are_names_equal() call will have gotten a match but we * still treat it correctly. */ goto found_it2; } /* * We have finished with this index buffer without success. Check for * the presence of a child node. */ if (ie->flags & INDEX_ENTRY_NODE) { if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { ntfs_log_debug("Index entry with child node found in a leaf " "node in directory inode 0x%llx.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } /* Child node present, descend into it. */ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8); if (vcn >= 0) goto descend_into_child_node; ntfs_log_debug("Negative child node vcn in directory inode " "0x%llx.\n", (unsigned long long)dir_ni->mft_no); errno = EIO; goto close_err_out; } free(ia); ntfs_attr_close(ia_na); ntfs_attr_put_search_ctx(ctx); /* * No child node present, return error code ENOENT, unless we have got * the mft reference of a matching name cached in mref in which case * return mref. */ if (mref) return mref; ntfs_log_debug("Entry not found.\n"); errno = ENOENT; return -1;put_err_out: eo = EIO; ntfs_log_debug("Corrupt directory. Aborting lookup.\n");eo_put_err_out: ntfs_attr_put_search_ctx(ctx); errno = eo; return -1;close_err_out: eo = errno; free(ia); ntfs_attr_close(ia_na); goto eo_put_err_out;}#ifndef __VISOPSYS__/** * ntfs_pathname_to_inode - Find the inode which represents the given pathname * @vol: An ntfs volume obtained from ntfs_mount * @parent: A directory inode to begin the search (may be NULL) * @pathname: Pathname to be located * * Take an ASCII pathname and find the inode that represents it. The function * splits the path and then descends the directory tree. If @parent is NULL, * then the root directory '.' will be used as the base for the search. * * Return: inode Success, the pathname was valid * NULL Error, the pathname was invalid, or some other error occurred */ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, const char *pathname){ u64 inum; int len, err = 0; char *p, *q; ntfs_inode *ni; ntfs_inode *result = NULL; ntfschar *unicode = NULL; char *ascii = NULL; if (!vol || !pathname) { errno = EINVAL; return NULL; } if (parent) { ni = parent; } else { ni = ntfs_inode_open(vol, FILE_root); if (!ni) { ntfs_log_debug("Couldn't open the inode of the root " "directory.\n"); err = EIO; goto close; } } unicode = calloc(1, MAX_PATH);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -