1N/A/**
1N/A * dir.c - Directory handling code. Part of the Linux-NTFS project.
1N/A *
1N/A * Copyright (c) 2002-2005 Anton Altaparmakov
1N/A * Copyright (c) 2005-2007 Yura Pakhuchiy
1N/A * Copyright (c) 2004-2005 Richard Russon
1N/A *
1N/A * This program/include file is free software; you can redistribute it and/or
1N/A * modify it under the terms of the GNU General Public License as published
1N/A * by the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program/include file is distributed in the hope that it will be
1N/A * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1N/A * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program (in the main directory of the Linux-NTFS
1N/A * distribution in the file COPYING); if not, write to the Free Software
1N/A * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1N/A */
1N/A
1N/A#ifdef HAVE_CONFIG_H
1N/A#include "config.h"
1N/A#endif
1N/A
1N/A#ifdef HAVE_STDLIB_H
1N/A#include <stdlib.h>
1N/A#endif
1N/A#ifdef HAVE_ERRNO_H
1N/A#include <errno.h>
1N/A#endif
1N/A#ifdef HAVE_STRING_H
1N/A#include <string.h>
1N/A#endif
1N/A#ifdef HAVE_SYS_STAT_H
1N/A#include <sys/stat.h>
1N/A#endif
1N/A
1N/A#ifdef HAVE_SYS_SYSMACROS_H
1N/A#include <sys/sysmacros.h>
1N/A#endif
1N/A
1N/A#include "compat.h"
1N/A#include "types.h"
1N/A#include "debug.h"
1N/A#include "attrib.h"
1N/A#include "inode.h"
1N/A#include "dir.h"
1N/A#include "volume.h"
1N/A#include "mft.h"
1N/A#include "index.h"
1N/A#include "ntfstime.h"
1N/A#include "lcnalloc.h"
1N/A#include "logging.h"
1N/A
1N/A/*
1N/A * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O"
1N/A * and "$Q" as global constants.
1N/A */
1N/Antfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'),
1N/A const_cpu_to_le16('3'), const_cpu_to_le16('0'),
1N/A const_cpu_to_le16('\0') };
1N/Antfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'),
1N/A const_cpu_to_le16('I'), const_cpu_to_le16('I'),
1N/A const_cpu_to_le16('\0') };
1N/Antfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'),
1N/A const_cpu_to_le16('D'), const_cpu_to_le16('H'),
1N/A const_cpu_to_le16('\0') };
1N/Antfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'),
1N/A const_cpu_to_le16('\0') };
1N/Antfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'),
1N/A const_cpu_to_le16('\0') };
1N/Antfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'),
1N/A const_cpu_to_le16('\0') };
1N/A
1N/A/**
1N/A * ntfs_inode_lookup_by_name - find an inode in a directory given its name
1N/A * @dir_ni: ntfs inode of the directory in which to search for the name
1N/A * @uname: Unicode name for which to search in the directory
1N/A * @uname_len: length of the name @uname in Unicode characters
1N/A *
1N/A * Look for an inode with name @uname in the directory with inode @dir_ni.
1N/A * ntfs_inode_lookup_by_name() walks the contents of the directory looking for
1N/A * the Unicode name. If the name is found in the directory, the corresponding
1N/A * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it
1N/A * is a 64-bit number containing the sequence number.
1N/A *
1N/A * On error, return -1 with errno set to the error code. If the inode is is not
1N/A * found errno is ENOENT.
1N/A *
1N/A * Note, @uname_len does not include the (optional) terminating NULL character.
1N/A *
1N/A * Note, we look for a case sensitive match first but we also look for a case
1N/A * insensitive match at the same time. If we find a case insensitive match, we
1N/A * save that for the case that we don't find an exact match, where we return
1N/A * the mft reference of the case insensitive match.
1N/A *
1N/A * If the volume is mounted with the case sensitive flag set, then we only
1N/A * allow exact matches.
1N/A */
1N/Au64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
1N/A const int uname_len)
1N/A{
1N/A VCN vcn;
1N/A u64 mref = 0;
1N/A s64 br;
1N/A ntfs_volume *vol = dir_ni->vol;
1N/A ntfs_attr_search_ctx *ctx;
1N/A INDEX_ROOT *ir;
1N/A INDEX_ENTRY *ie;
1N/A INDEX_ALLOCATION *ia;
1N/A u8 *index_end;
1N/A ntfs_attr *ia_na;
1N/A int eo, rc;
1N/A u32 index_block_size, index_vcn_size;
1N/A u8 index_vcn_size_bits;
1N/A
1N/A if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(dir_ni, NULL);
1N/A if (!ctx)
1N/A return -1;
1N/A
1N/A /* Find the index root attribute in the mft record. */
1N/A if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A ntfs_log_perror("Index root attribute missing in directory "
1N/A "inode 0x%llx", (unsigned long long)dir_ni->
1N/A mft_no);
1N/A goto put_err_out;
1N/A }
1N/A /* Get to the index root value. */
1N/A ir = (INDEX_ROOT*)((u8*)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A index_block_size = le32_to_cpu(ir->index_block_size);
1N/A if (index_block_size < NTFS_BLOCK_SIZE ||
1N/A index_block_size & (index_block_size - 1)) {
1N/A ntfs_log_debug("Index block size %u is invalid.\n",
1N/A (unsigned)index_block_size);
1N/A goto put_err_out;
1N/A }
1N/A index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
1N/A /* The first index entry. */
1N/A ie = (INDEX_ENTRY*)((u8*)&ir->index +
1N/A le32_to_cpu(ir->index.entries_offset));
1N/A /*
1N/A * Loop until we exceed valid memory (corruption case) or until we
1N/A * reach the last entry.
1N/A */
1N/A for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
1N/A /* Bounds checks. */
1N/A if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
1N/A sizeof(INDEX_ENTRY_HEADER) > index_end ||
1N/A (u8*)ie + le16_to_cpu(ie->key_length) >
1N/A index_end)
1N/A goto put_err_out;
1N/A /*
1N/A * The last entry cannot contain a name. It can however contain
1N/A * a pointer to a child node in the B+tree so we just break out.
1N/A */
1N/A if (ie->flags & INDEX_ENTRY_END)
1N/A break;
1N/A /*
1N/A * We perform a case sensitive comparison and if that matches
1N/A * we are done and return the mft reference of the inode (i.e.
1N/A * the inode number together with the sequence number for
1N/A * consistency checking). We convert it to cpu format before
1N/A * returning.
1N/A */
1N/A if (ntfs_names_are_equal(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length,
1N/A CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
1N/Afound_it:
1N/A /*
1N/A * We have a perfect match, so we don't need to care
1N/A * about having matched imperfectly before.
1N/A */
1N/A mref = le64_to_cpu(ie->u.indexed_file);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return mref;
1N/A }
1N/A /*
1N/A * For a case insensitive mount, we also perform a case
1N/A * insensitive comparison. If the comparison matches, we cache
1N/A * the mft reference in mref. Use first case insensitive match
1N/A * in case if no name matches case sensitive, but several names
1N/A * matches case insensitive.
1N/A */
1N/A if (!mref && !NVolCaseSensitive(vol) &&
1N/A ntfs_names_are_equal(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length,
1N/A IGNORE_CASE, vol->upcase, vol->upcase_len))
1N/A mref = le64_to_cpu(ie->u.indexed_file);
1N/A /*
1N/A * Not a perfect match, need to do full blown collation so we
1N/A * know which way in the B+tree we have to go.
1N/A */
1N/A rc = ntfs_names_collate(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length, 1,
1N/A IGNORE_CASE, vol->upcase, vol->upcase_len);
1N/A /*
1N/A * If uname collates before the name of the current entry, there
1N/A * is definitely no such name in this index but we might need to
1N/A * descend into the B+tree so we just break out of the loop.
1N/A */
1N/A if (rc == -1)
1N/A break;
1N/A /* The names are not equal, continue the search. */
1N/A if (rc)
1N/A continue;
1N/A /*
1N/A * Names match with case insensitive comparison, now try the
1N/A * case sensitive comparison, which is required for proper
1N/A * collation.
1N/A */
1N/A rc = ntfs_names_collate(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length, 1,
1N/A CASE_SENSITIVE, vol->upcase, vol->upcase_len);
1N/A if (rc == -1)
1N/A break;
1N/A if (rc)
1N/A continue;
1N/A /*
1N/A * Perfect match, this will never happen as the
1N/A * ntfs_are_names_equal() call will have gotten a match but we
1N/A * still treat it correctly.
1N/A */
1N/A goto found_it;
1N/A }
1N/A /*
1N/A * We have finished with this index without success. Check for the
1N/A * presence of a child node and if not present return error code
1N/A * ENOENT, unless we have got the mft reference of a matching name
1N/A * cached in mref in which case return mref.
1N/A */
1N/A if (!(ie->flags & INDEX_ENTRY_NODE)) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A if (mref)
1N/A return mref;
1N/A ntfs_log_debug("Entry not found.\n");
1N/A errno = ENOENT;
1N/A return -1;
1N/A } /* Child node present, descend into it. */
1N/A
1N/A /* Open the index allocation attribute. */
1N/A ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
1N/A if (!ia_na) {
1N/A ntfs_log_perror("Failed to open index allocation attribute. "
1N/A "Directory inode 0x%llx is corrupt or driver "
1N/A "bug", (unsigned long long)dir_ni->mft_no);
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Allocate a buffer for the current index block. */
1N/A ia = (INDEX_ALLOCATION*)malloc(index_block_size);
1N/A if (!ia) {
1N/A ntfs_log_perror("Failed to allocate buffer for index block");
1N/A ntfs_attr_close(ia_na);
1N/A goto put_err_out;
1N/A }
1N/A
1N/A /* Determine the size of a vcn in the directory index. */
1N/A if (vol->cluster_size <= index_block_size) {
1N/A index_vcn_size = vol->cluster_size;
1N/A index_vcn_size_bits = vol->cluster_size_bits;
1N/A } else {
1N/A index_vcn_size = vol->sector_size;
1N/A index_vcn_size_bits = vol->sector_size_bits;
1N/A }
1N/A
1N/A /* Get the starting vcn of the index_block holding the child node. */
1N/A vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
1N/A
1N/Adescend_into_child_node:
1N/A
1N/A /* Read the index block starting at vcn. */
1N/A br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1,
1N/A index_block_size, ia);
1N/A if (br != 1) {
1N/A if (br != -1)
1N/A errno = EIO;
1N/A ntfs_log_perror("Failed to read vcn 0x%llx",
1N/A (unsigned long long)vcn);
1N/A goto close_err_out;
1N/A }
1N/A
1N/A if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
1N/A ntfs_log_debug("Actual VCN (0x%llx) of index buffer is "
1N/A "different from expected VCN (0x%llx).\n",
1N/A (long long)sle64_to_cpu(ia->index_block_vcn),
1N/A (long long)vcn);
1N/A errno = EIO;
1N/A goto close_err_out;
1N/A }
1N/A if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
1N/A ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode "
1N/A "0x%llx has a size (%u) differing from the "
1N/A "directory specified size (%u).\n",
1N/A (long long)vcn, (unsigned long long)dir_ni->
1N/A mft_no, (unsigned)le32_to_cpu(ia->index.
1N/A allocated_size) + 0x18, (unsigned)
1N/A index_block_size);
1N/A errno = EIO;
1N/A goto close_err_out;
1N/A }
1N/A index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
1N/A if (index_end > (u8*)ia + index_block_size) {
1N/A ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory "
1N/A "inode 0x%llx exceeds maximum size.\n",
1N/A (long long)vcn, (unsigned long long)dir_ni->
1N/A mft_no);
1N/A errno = EIO;
1N/A goto close_err_out;
1N/A }
1N/A
1N/A /* The first index entry. */
1N/A ie = (INDEX_ENTRY*)((u8*)&ia->index +
1N/A le32_to_cpu(ia->index.entries_offset));
1N/A /*
1N/A * Iterate similar to above big loop but applied to index buffer, thus
1N/A * loop until we exceed valid memory (corruption case) or until we
1N/A * reach the last entry.
1N/A */
1N/A for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
1N/A /* Bounds check. */
1N/A if ((u8*)ie < (u8*)ia || (u8*)ie +
1N/A sizeof(INDEX_ENTRY_HEADER) > index_end ||
1N/A (u8*)ie + le16_to_cpu(ie->key_length) >
1N/A index_end) {
1N/A ntfs_log_debug("Index entry out of bounds in directory "
1N/A "inode 0x%llx.\n",
1N/A (unsigned long long)dir_ni->mft_no);
1N/A errno = EIO;
1N/A goto close_err_out;
1N/A }
1N/A /*
1N/A * The last entry cannot contain a name. It can however contain
1N/A * a pointer to a child node in the B+tree so we just break out.
1N/A */
1N/A if (ie->flags & INDEX_ENTRY_END)
1N/A break;
1N/A /*
1N/A * We perform a case sensitive comparison and if that matches
1N/A * we are done and return the mft reference of the inode (i.e.
1N/A * the inode number together with the sequence number for
1N/A * consistency checking). We convert it to cpu format before
1N/A * returning.
1N/A */
1N/A if (ntfs_names_are_equal(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length,
1N/A CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
1N/Afound_it2:
1N/A /*
1N/A * We have a perfect match, so we don't need to care
1N/A * about having matched imperfectly before.
1N/A */
1N/A mref = le64_to_cpu(ie->u.indexed_file);
1N/A free(ia);
1N/A ntfs_attr_close(ia_na);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return mref;
1N/A }
1N/A /*
1N/A * For a case insensitive mount, we also perform a case
1N/A * insensitive comparison. If the comparison matches, we cache
1N/A * the mft reference in mref. Use first case insensitive match
1N/A * in case if no name matches case sensitive, but several names
1N/A * matches case insensitive.
1N/A */
1N/A if (!mref && !NVolCaseSensitive(vol) &&
1N/A ntfs_names_are_equal(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length,
1N/A IGNORE_CASE, vol->upcase, vol->upcase_len))
1N/A mref = le64_to_cpu(ie->u.indexed_file);
1N/A /*
1N/A * Not a perfect match, need to do full blown collation so we
1N/A * know which way in the B+tree we have to go.
1N/A */
1N/A rc = ntfs_names_collate(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length, 1,
1N/A IGNORE_CASE, vol->upcase, vol->upcase_len);
1N/A /*
1N/A * If uname collates before the name of the current entry, there
1N/A * is definitely no such name in this index but we might need to
1N/A * descend into the B+tree so we just break out of the loop.
1N/A */
1N/A if (rc == -1)
1N/A break;
1N/A /* The names are not equal, continue the search. */
1N/A if (rc)
1N/A continue;
1N/A /*
1N/A * Names match with case insensitive comparison, now try the
1N/A * case sensitive comparison, which is required for proper
1N/A * collation.
1N/A */
1N/A rc = ntfs_names_collate(uname, uname_len,
1N/A (ntfschar*)&ie->key.file_name.file_name,
1N/A ie->key.file_name.file_name_length, 1,
1N/A CASE_SENSITIVE, vol->upcase, vol->upcase_len);
1N/A if (rc == -1)
1N/A break;
1N/A if (rc)
1N/A continue;
1N/A /*
1N/A * Perfect match, this will never happen as the
1N/A * ntfs_are_names_equal() call will have gotten a match but we
1N/A * still treat it correctly.
1N/A */
1N/A goto found_it2;
1N/A }
1N/A /*
1N/A * We have finished with this index buffer without success. Check for
1N/A * the presence of a child node.
1N/A */
1N/A if (ie->flags & INDEX_ENTRY_NODE) {
1N/A if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
1N/A ntfs_log_debug("Index entry with child node found in a "
1N/A "leaf node in directory inode "
1N/A "0x%llx.\n",
1N/A (unsigned long long)dir_ni->mft_no);
1N/A errno = EIO;
1N/A goto close_err_out;
1N/A }
1N/A /* Child node present, descend into it. */
1N/A vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
1N/A if (vcn >= 0)
1N/A goto descend_into_child_node;
1N/A ntfs_log_debug("Negative child node vcn in directory inode "
1N/A "0x%llx.\n", (unsigned long long)dir_ni->
1N/A mft_no);
1N/A errno = EIO;
1N/A goto close_err_out;
1N/A }
1N/A free(ia);
1N/A ntfs_attr_close(ia_na);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A /*
1N/A * No child node present, return error code ENOENT, unless we have got
1N/A * the mft reference of a matching name cached in mref in which case
1N/A * return mref.
1N/A */
1N/A if (mref)
1N/A return mref;
1N/A ntfs_log_debug("Entry not found.\n");
1N/A errno = ENOENT;
1N/A return -1;
1N/Aput_err_out:
1N/A eo = EIO;
1N/A ntfs_log_debug("Corrupt directory. Aborting lookup.\n");
1N/Aeo_put_err_out:
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = eo;
1N/A return -1;
1N/Aclose_err_out:
1N/A eo = errno;
1N/A free(ia);
1N/A ntfs_attr_close(ia_na);
1N/A goto eo_put_err_out;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_pathname_to_inode_num - find the inode number which represents the
1N/A * given pathname
1N/A * @vol: An ntfs volume obtained from ntfs_mount
1N/A * @parent: A directory inode to begin the search (may be NULL)
1N/A * @pathname: Pathname to be located
1N/A *
1N/A * Take an ASCII pathname and find the inode that represents it. The function
1N/A * splits the path and then descends the directory tree. If @parent is NULL,
1N/A * then the root directory '.' will be used as the base for the search.
1N/A *
1N/A * Return: -1 Error, the pathname was invalid, or some other error occurred
1N/A * else Success, the pathname was valid
1N/A */
1N/Au64 ntfs_pathname_to_inode_num(ntfs_volume *vol, ntfs_inode *parent,
1N/A const char *pathname)
1N/A{
1N/A u64 inum, result;
1N/A int len, err = 0;
1N/A char *p, *q;
1N/A ntfs_inode *ni = NULL;
1N/A ntfschar *unicode = NULL;
1N/A char *ascii = NULL;
1N/A
1N/A inum = result = (u64)-1;
1N/A if (!vol || !pathname) {
1N/A err = EINVAL;
1N/A goto close;
1N/A }
1N/A ntfs_log_trace("Path: '%s'\n", pathname);
1N/A if (parent) {
1N/A ni = parent;
1N/A } else
1N/A inum = FILE_root;
1N/A unicode = calloc(1, MAX_PATH);
1N/A ascii = strdup(pathname);
1N/A if (!unicode || !ascii) {
1N/A ntfs_log_error("Out of memory.\n");
1N/A err = ENOMEM;
1N/A goto close;
1N/A }
1N/A p = ascii;
1N/A /* Remove leading /'s. */
1N/A while (p && *p == PATH_SEP)
1N/A p++;
1N/A while (p && *p) {
1N/A if (!ni) {
1N/A ni = ntfs_inode_open(vol, inum);
1N/A if (!ni) {
1N/A ntfs_log_debug("Cannot open inode %llu.\n",
1N/A (unsigned long long)inum);
1N/A err = EIO;
1N/A goto close;
1N/A }
1N/A }
1N/A /* Find the end of the first token. */
1N/A q = strchr(p, PATH_SEP);
1N/A if (q != NULL) {
1N/A *q = 0;
1N/A q++;
1N/A }
1N/A len = ntfs_mbstoucs(p, &unicode, MAX_PATH);
1N/A if (len < 0) {
1N/A ntfs_log_debug("Couldn't convert name to Unicode: "
1N/A "%s.\n", p);
1N/A err = EILSEQ;
1N/A goto close;
1N/A }
1N/A inum = ntfs_inode_lookup_by_name(ni, unicode, len);
1N/A if (inum == (u64)-1) {
1N/A ntfs_log_debug("Couldn't find name '%s' in pathname "
1N/A "'%s'.\n", p, pathname);
1N/A err = ENOENT;
1N/A goto close;
1N/A }
1N/A inum = MREF(inum);
1N/A if (ni != parent)
1N/A ntfs_inode_close(ni);
1N/A ni = NULL;
1N/A p = q;
1N/A while (p && *p == PATH_SEP)
1N/A p++;
1N/A }
1N/A result = inum;
1N/Aclose:
1N/A if (ni && (ni != parent))
1N/A ntfs_inode_close(ni);
1N/A free(ascii);
1N/A free(unicode);
1N/A if (err)
1N/A errno = err;
1N/A return result;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_pathname_to_inode - Find the inode which represents the given pathname
1N/A * @vol: An ntfs volume obtained from ntfs_mount
1N/A * @parent: A directory inode to begin the search (may be NULL)
1N/A * @pathname: Pathname to be located
1N/A *
1N/A * Take an ASCII pathname and find the inode that represents it. The function
1N/A * splits the path and then descends the directory tree. If @parent is NULL,
1N/A * then the root directory '.' will be used as the base for the search.
1N/A *
1N/A * Return: inode Success, the pathname was valid
1N/A * NULL Error, the pathname was invalid, or some other error occurred
1N/A */
1N/Antfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
1N/A const char *pathname)
1N/A{
1N/A u64 inum;
1N/A
1N/A inum = ntfs_pathname_to_inode_num(vol, parent, pathname);
1N/A if (inum == (u64)-1)
1N/A return NULL;
1N/A return ntfs_inode_open(vol, inum);
1N/A}
1N/A
1N/A/*
1N/A * The little endian Unicode string ".." for ntfs_readdir().
1N/A */
1N/Astatic const ntfschar dotdot[3] = { const_cpu_to_le16('.'),
1N/A const_cpu_to_le16('.'),
1N/A const_cpu_to_le16('\0') };
1N/A
1N/A/**
1N/A * ntfs_filldir - ntfs specific filldir method
1N/A * @vol: ntfs volume with wjich we are working
1N/A * @pos: current position in directory
1N/A * @ie: current index entry
1N/A * @dirent: context for filldir callback supplied by the caller
1N/A * @filldir: filldir callback supplied by the caller
1N/A *
1N/A * Pass information specifying the current directory entry @ie to the @filldir
1N/A * callback.
1N/A */
1N/Astatic int ntfs_filldir(ntfs_volume *vol, s64 *pos, INDEX_ENTRY *ie,
1N/A void *dirent, ntfs_filldir_t filldir)
1N/A{
1N/A FILE_NAME_ATTR *fn = &ie->key.file_name;
1N/A unsigned dt_type;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A /* Skip root directory self reference entry. */
1N/A if (MREF_LE(ie->u.indexed_file) == FILE_root)
1N/A return 0;
1N/A if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT)
1N/A dt_type = NTFS_DT_DIR;
1N/A else {
1N/A if (NVolInterix(vol) && fn->file_attributes & FILE_ATTR_SYSTEM)
1N/A dt_type = NTFS_DT_UNKNOWN;
1N/A else
1N/A dt_type = NTFS_DT_REG;
1N/A }
1N/A return filldir(dirent, fn->file_name, fn->file_name_length,
1N/A fn->file_name_type, *pos,
1N/A le64_to_cpu(ie->u.indexed_file), dt_type);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode
1N/A * @ni: ntfs inode whose parent directory to find
1N/A *
1N/A * Find the parent directory of the ntfs inode @ni. To do this, find the first
1N/A * file name attribute in the mft record of @ni and return the parent mft
1N/A * reference from that.
1N/A *
1N/A * Note this only makes sense for directories, since files can be hard linked
1N/A * from multiple directories and there is no way for us to tell which one is
1N/A * being looked for.
1N/A *
1N/A * Technically directories can have hard links, too, but we consider that as
1N/A * illegal as Linux/UNIX do not support directory hard links.
1N/A *
1N/A * Return the mft reference of the parent directory on success or -1 on error
1N/A * with errno set to the error code.
1N/A */
1N/Astatic MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
1N/A{
1N/A MFT_REF mref;
1N/A ntfs_attr_search_ctx *ctx;
1N/A FILE_NAME_ATTR *fn;
1N/A int eo;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A if (!ni) {
1N/A errno = EINVAL;
1N/A return ERR_MREF(-1);
1N/A }
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!ctx)
1N/A return ERR_MREF(-1);
1N/A if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
1N/A ntfs_log_debug("No file name found in inode 0x%llx. Corrupt "
1N/A "inode.\n", (unsigned long long)ni->mft_no);
1N/A goto err_out;
1N/A }
1N/A if (ctx->attr->non_resident) {
1N/A ntfs_log_debug("File name attribute must be resident. "
1N/A "Corrupt inode 0x%llx.\n",
1N/A (unsigned long long)ni->mft_no);
1N/A goto io_err_out;
1N/A }
1N/A fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A if ((u8*)fn + le32_to_cpu(ctx->attr->u.res.value_length) >
1N/A (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
1N/A ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n",
1N/A (unsigned long long)ni->mft_no);
1N/A goto io_err_out;
1N/A }
1N/A mref = le64_to_cpu(fn->parent_directory);
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A return mref;
1N/Aio_err_out:
1N/A errno = EIO;
1N/Aerr_out:
1N/A eo = errno;
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A errno = eo;
1N/A return ERR_MREF(-1);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_readdir - read the contents of an ntfs directory
1N/A * @dir_ni: ntfs inode of current directory
1N/A * @pos: current position in directory
1N/A * @dirent: context for filldir callback supplied by the caller
1N/A * @filldir: filldir callback supplied by the caller
1N/A *
1N/A * Parse the index root and the index blocks that are marked in use in the
1N/A * index bitmap and hand each found directory entry to the @filldir callback
1N/A * supplied by the caller.
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A *
1N/A * Note: Index blocks are parsed in ascending vcn order, from which follows
1N/A * that the directory entries are not returned sorted.
1N/A */
1N/Aint ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
1N/A void *dirent, ntfs_filldir_t filldir)
1N/A{
1N/A s64 i_size, br, ia_pos, bmp_pos, ia_start;
1N/A ntfs_volume *vol;
1N/A ntfs_attr *ia_na, *bmp_na = NULL;
1N/A ntfs_attr_search_ctx *ctx = NULL;
1N/A u8 *index_end, *bmp = NULL;
1N/A INDEX_ROOT *ir;
1N/A INDEX_ENTRY *ie;
1N/A INDEX_ALLOCATION *ia = NULL;
1N/A int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo;
1N/A u32 index_block_size, index_vcn_size;
1N/A u8 index_block_size_bits, index_vcn_size_bits;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A if (!dir_ni || !pos || !filldir) {
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A
1N/A if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
1N/A errno = ENOTDIR;
1N/A return -1;
1N/A }
1N/A
1N/A vol = dir_ni->vol;
1N/A
1N/A ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n",
1N/A (unsigned long long)dir_ni->mft_no, (long long)*pos);
1N/A
1N/A /* Open the index allocation attribute. */
1N/A ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
1N/A if (!ia_na) {
1N/A if (errno != ENOENT) {
1N/A ntfs_log_perror("Failed to open index allocation "
1N/A "attribute. Directory inode 0x%llx is "
1N/A "corrupt or bug", (unsigned long long)
1N/A dir_ni->mft_no);
1N/A return -1;
1N/A }
1N/A i_size = 0;
1N/A } else
1N/A i_size = ia_na->data_size;
1N/A
1N/A rc = 0;
1N/A
1N/A /* Are we at end of dir yet? */
1N/A if (*pos >= i_size + vol->mft_record_size)
1N/A goto done;
1N/A
1N/A /* Emulate . and .. for all directories. */
1N/A if (!*pos) {
1N/A rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos,
1N/A MK_MREF(dir_ni->mft_no,
1N/A le16_to_cpu(dir_ni->mrec->sequence_number)),
1N/A NTFS_DT_DIR);
1N/A if (rc)
1N/A goto err_out;
1N/A ++*pos;
1N/A }
1N/A if (*pos == 1) {
1N/A MFT_REF parent_mref;
1N/A
1N/A parent_mref = ntfs_mft_get_parent_ref(dir_ni);
1N/A if (parent_mref == ERR_MREF(-1)) {
1N/A ntfs_log_perror("Parent directory not found");
1N/A goto dir_err_out;
1N/A }
1N/A
1N/A rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos,
1N/A parent_mref, NTFS_DT_DIR);
1N/A if (rc)
1N/A goto err_out;
1N/A ++*pos;
1N/A }
1N/A
1N/A ctx = ntfs_attr_get_search_ctx(dir_ni, NULL);
1N/A if (!ctx)
1N/A goto err_out;
1N/A
1N/A /* Get the offset into the index root attribute. */
1N/A ir_pos = (int)*pos;
1N/A /* Find the index root attribute in the mft record. */
1N/A if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE,
1N/A 0, NULL, 0, ctx)) {
1N/A ntfs_log_debug("Index root attribute missing in directory "
1N/A "inode 0x%llx.\n", (unsigned long long)dir_ni->
1N/A mft_no);
1N/A goto dir_err_out;
1N/A }
1N/A /* Get to the index root value. */
1N/A ir = (INDEX_ROOT*)((u8*)ctx->attr +
1N/A le16_to_cpu(ctx->attr->u.res.value_offset));
1N/A
1N/A /* Determine the size of a vcn in the directory index. */
1N/A index_block_size = le32_to_cpu(ir->index_block_size);
1N/A if (index_block_size < NTFS_BLOCK_SIZE ||
1N/A index_block_size & (index_block_size - 1)) {
1N/A ntfs_log_debug("Index block size %u is invalid.\n",
1N/A (unsigned)index_block_size);
1N/A goto dir_err_out;
1N/A }
1N/A index_block_size_bits = ffs(index_block_size) - 1;
1N/A if (vol->cluster_size <= index_block_size) {
1N/A index_vcn_size = vol->cluster_size;
1N/A index_vcn_size_bits = vol->cluster_size_bits;
1N/A } else {
1N/A index_vcn_size = vol->sector_size;
1N/A index_vcn_size_bits = vol->sector_size_bits;
1N/A }
1N/A
1N/A /* Are we jumping straight into the index allocation attribute? */
1N/A if (*pos >= vol->mft_record_size) {
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ctx = NULL;
1N/A goto skip_index_root;
1N/A }
1N/A
1N/A index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
1N/A /* The first index entry. */
1N/A ie = (INDEX_ENTRY*)((u8*)&ir->index +
1N/A le32_to_cpu(ir->index.entries_offset));
1N/A /*
1N/A * Loop until we exceed valid memory (corruption case) or until we
1N/A * reach the last entry or until filldir tells us it has had enough
1N/A * or signals an error (both covered by the rc test).
1N/A */
1N/A for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
1N/A ntfs_log_debug("In index root, offset 0x%x.\n",
1N/A (u8*)ie - (u8*)ir);
1N/A /* Bounds checks. */
1N/A if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
1N/A sizeof(INDEX_ENTRY_HEADER) > index_end ||
1N/A (u8*)ie + le16_to_cpu(ie->key_length) >
1N/A index_end)
1N/A goto dir_err_out;
1N/A /* The last entry cannot contain a name. */
1N/A if (ie->flags & INDEX_ENTRY_END)
1N/A break;
1N/A /* Skip index root entry if continuing previous readdir. */
1N/A if (ir_pos > (u8*)ie - (u8*)ir)
1N/A continue;
1N/A /* Advance the position even if going to skip the entry. */
1N/A *pos = (u8*)ie - (u8*)ir;
1N/A /*
1N/A * Submit the directory entry to ntfs_filldir(), which will
1N/A * invoke the filldir() callback as appropriate.
1N/A */
1N/A rc = ntfs_filldir(vol, pos, ie, dirent, filldir);
1N/A if (rc)
1N/A goto err_out;
1N/A }
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A ctx = NULL;
1N/A
1N/A /* If there is no index allocation attribute we are finished. */
1N/A if (!ia_na)
1N/A goto EOD;
1N/A
1N/A /* Advance *pos to the beginning of the index allocation. */
1N/A *pos = vol->mft_record_size;
1N/A
1N/Askip_index_root:
1N/A
1N/A if (!ia_na)
1N/A goto done;
1N/A
1N/A /* Allocate a buffer for the current index block. */
1N/A ia = (INDEX_ALLOCATION*)malloc(index_block_size);
1N/A if (!ia) {
1N/A ntfs_log_perror("Failed to allocate buffer for index block");
1N/A goto err_out;
1N/A }
1N/A
1N/A bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4);
1N/A if (!bmp_na) {
1N/A ntfs_log_perror("Failed to open index bitmap attribute");
1N/A goto dir_err_out;
1N/A }
1N/A
1N/A /* Get the offset into the index allocation attribute. */
1N/A ia_pos = *pos - vol->mft_record_size;
1N/A
1N/A bmp_pos = ia_pos >> index_block_size_bits;
1N/A if (bmp_pos >> 3 >= bmp_na->data_size) {
1N/A ntfs_log_debug("Current index position exceeds index bitmap "
1N/A "size.\n");
1N/A goto dir_err_out;
1N/A }
1N/A
1N/A bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096);
1N/A bmp = (u8*)malloc(bmp_buf_size);
1N/A if (!bmp) {
1N/A ntfs_log_perror("Failed to allocate bitmap buffer");
1N/A goto err_out;
1N/A }
1N/A
1N/A br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
1N/A if (br != bmp_buf_size) {
1N/A if (br != -1)
1N/A errno = EIO;
1N/A ntfs_log_perror("Failed to read from index bitmap attribute");
1N/A goto err_out;
1N/A }
1N/A
1N/A bmp_buf_pos = 0;
1N/A /* If the index block is not in use find the next one that is. */
1N/A while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) {
1N/Afind_next_index_buffer:
1N/A bmp_pos++;
1N/A bmp_buf_pos++;
1N/A /* If we have reached the end of the bitmap, we are done. */
1N/A if (bmp_pos >> 3 >= bmp_na->data_size)
1N/A goto EOD;
1N/A ia_pos = bmp_pos << index_block_size_bits;
1N/A if (bmp_buf_pos >> 3 < bmp_buf_size)
1N/A continue;
1N/A /* Read next chunk from the index bitmap. */
1N/A if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size)
1N/A bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3);
1N/A br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
1N/A if (br != bmp_buf_size) {
1N/A if (br != -1)
1N/A errno = EIO;
1N/A ntfs_log_perror("Failed to read from index bitmap "
1N/A "attribute");
1N/A goto err_out;
1N/A }
1N/A }
1N/A
1N/A ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos);
1N/A
1N/A /* Read the index block starting at bmp_pos. */
1N/A br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1,
1N/A index_block_size, ia);
1N/A if (br != 1) {
1N/A if (br != -1)
1N/A errno = EIO;
1N/A ntfs_log_perror("Failed to read index block");
1N/A goto err_out;
1N/A }
1N/A
1N/A ia_start = ia_pos & ~(s64)(index_block_size - 1);
1N/A if (sle64_to_cpu(ia->index_block_vcn) != ia_start >>
1N/A index_vcn_size_bits) {
1N/A ntfs_log_debug("Actual VCN (0x%llx) of index buffer is "
1N/A "different from expected VCN (0x%llx) in "
1N/A "inode 0x%llx.\n",
1N/A (long long)sle64_to_cpu(ia->index_block_vcn),
1N/A (long long)ia_start >> index_vcn_size_bits,
1N/A (unsigned long long)dir_ni->mft_no);
1N/A goto dir_err_out;
1N/A }
1N/A if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
1N/A ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode "
1N/A "0x%llx has a size (%u) differing from the "
1N/A "directory specified size (%u).\n",
1N/A (long long)ia_start >> index_vcn_size_bits,
1N/A (unsigned long long)dir_ni->mft_no,
1N/A (unsigned) le32_to_cpu(ia->index.allocated_size)
1N/A + 0x18, (unsigned)index_block_size);
1N/A goto dir_err_out;
1N/A }
1N/A index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
1N/A if (index_end > (u8*)ia + index_block_size) {
1N/A ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory "
1N/A "inode 0x%llx exceeds maximum size.\n",
1N/A (long long)ia_start >> index_vcn_size_bits,
1N/A (unsigned long long)dir_ni->mft_no);
1N/A goto dir_err_out;
1N/A }
1N/A /* The first index entry. */
1N/A ie = (INDEX_ENTRY*)((u8*)&ia->index +
1N/A le32_to_cpu(ia->index.entries_offset));
1N/A /*
1N/A * Loop until we exceed valid memory (corruption case) or until we
1N/A * reach the last entry or until ntfs_filldir tells us it has had
1N/A * enough or signals an error (both covered by the rc test).
1N/A */
1N/A for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
1N/A ntfs_log_debug("In index allocation, offset 0x%llx.\n",
1N/A (long long)ia_start + ((u8*)ie - (u8*)ia));
1N/A /* Bounds checks. */
1N/A if ((u8*)ie < (u8*)ia || (u8*)ie +
1N/A sizeof(INDEX_ENTRY_HEADER) > index_end ||
1N/A (u8*)ie + le16_to_cpu(ie->key_length) >
1N/A index_end) {
1N/A ntfs_log_debug("Index entry out of bounds in directory "
1N/A "inode 0x%llx.\n", (unsigned long long)
1N/A dir_ni->mft_no);
1N/A goto dir_err_out;
1N/A }
1N/A /* The last entry cannot contain a name. */
1N/A if (ie->flags & INDEX_ENTRY_END)
1N/A break;
1N/A /* Skip index entry if continuing previous readdir. */
1N/A if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
1N/A continue;
1N/A /* Advance the position even if going to skip the entry. */
1N/A *pos = (u8*)ie - (u8*)ia + (sle64_to_cpu(
1N/A ia->index_block_vcn) << index_vcn_size_bits) +
1N/A dir_ni->vol->mft_record_size;
1N/A /*
1N/A * Submit the directory entry to ntfs_filldir(), which will
1N/A * invoke the filldir() callback as appropriate.
1N/A */
1N/A rc = ntfs_filldir(vol, pos, ie, dirent, filldir);
1N/A if (rc)
1N/A goto err_out;
1N/A }
1N/A goto find_next_index_buffer;
1N/AEOD:
1N/A /* We are finished, set *pos to EOD. */
1N/A *pos = i_size + vol->mft_record_size;
1N/Adone:
1N/A free(ia);
1N/A free(bmp);
1N/A if (bmp_na)
1N/A ntfs_attr_close(bmp_na);
1N/A if (ia_na)
1N/A ntfs_attr_close(ia_na);
1N/A ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos);
1N/A return 0;
1N/Adir_err_out:
1N/A errno = EIO;
1N/Aerr_out:
1N/A eo = errno;
1N/A if (rc)
1N/A ntfs_log_trace("filldir returned %i, *pos 0x%llx.", rc,
1N/A (long long)*pos);
1N/A ntfs_log_trace("Failed.\n");
1N/A if (ctx)
1N/A ntfs_attr_put_search_ctx(ctx);
1N/A free(ia);
1N/A free(bmp);
1N/A if (bmp_na)
1N/A ntfs_attr_close(bmp_na);
1N/A if (ia_na)
1N/A ntfs_attr_close(ia_na);
1N/A errno = eo;
1N/A return -1;
1N/A}
1N/A
1N/A/**
1N/A * __ntfs_create - create object on ntfs volume
1N/A * @dir_ni: ntfs inode for directory in which create new object
1N/A * @name: unicode name of new object
1N/A * @name_len: length of the name in unicode characters
1N/A * @type: type of the object to create
1N/A * @dev: major and minor device numbers (obtained from makedev())
1N/A * @target: target in unicode (only for symlinks)
1N/A * @target_len: length of target in unicode characters
1N/A *
1N/A * Internal, use ntfs_create{,_device,_symlink} wrappers instead.
1N/A *
1N/A * @type can be:
1N/A * S_IFREG to create regular file
1N/A * S_IFDIR to create directory
1N/A * S_IFBLK to create block device
1N/A * S_IFCHR to create character device
1N/A * S_IFLNK to create symbolic link
1N/A * S_IFIFO to create FIFO
1N/A * S_IFSOCK to create socket
1N/A * other values are invalid.
1N/A *
1N/A * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value
1N/A * ignored.
1N/A *
1N/A * @target and @target_len are used only if @type is S_IFLNK, in other cases
1N/A * their value ignored.
1N/A *
1N/A * Return opened ntfs inode that describes created object on success or NULL
1N/A * on error with errno set to the error code.
1N/A */
1N/Astatic ntfs_inode *__ntfs_create(ntfs_inode *dir_ni,
1N/A ntfschar *name, u8 name_len, dev_t type, dev_t dev,
1N/A ntfschar *target, u8 target_len)
1N/A{
1N/A ntfs_inode *ni;
1N/A int rollback_data = 0, rollback_sd = 0;
1N/A FILE_NAME_ATTR *fn = NULL;
1N/A STANDARD_INFORMATION *si = NULL;
1N/A SECURITY_DESCRIPTOR_ATTR *sd = NULL;
1N/A ACL *acl;
1N/A ACCESS_ALLOWED_ACE *ace;
1N/A SID *sid;
1N/A int err, fn_len, si_len, sd_len;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A /* Sanity checks. */
1N/A if (!dir_ni || !name || !name_len) {
1N/A ntfs_log_error("Invalid arguments.\n");
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A /* FIXME: Reparse points requires special handling. */
1N/A if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) {
1N/A errno = EOPNOTSUPP;
1N/A return NULL;
1N/A }
1N/A /* Allocate MFT record for new file. */
1N/A ni = ntfs_mft_record_alloc(dir_ni->vol, NULL);
1N/A if (!ni) {
1N/A ntfs_log_error("Failed to allocate new MFT record: %s.\n",
1N/A strerror(errno));
1N/A return NULL;
1N/A }
1N/A /*
1N/A * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION
1N/A * version 1.2, windows will upgrade it to version 3 if needed.
1N/A */
1N/A si_len = offsetof(STANDARD_INFORMATION, u.v12.v1_end);
1N/A si = calloc(1, si_len);
1N/A if (!si) {
1N/A err = errno;
1N/A ntfs_log_error("Not enough memory.\n");
1N/A goto err_out;
1N/A }
1N/A si->creation_time = utc2ntfs(ni->creation_time);
1N/A si->last_data_change_time = utc2ntfs(ni->last_data_change_time);
1N/A si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
1N/A si->last_access_time = utc2ntfs(ni->last_access_time);
1N/A if (!S_ISREG(type) && !S_ISDIR(type)) {
1N/A si->file_attributes = FILE_ATTR_SYSTEM;
1N/A ni->flags = FILE_ATTR_SYSTEM;
1N/A }
1N/A /* Add STANDARD_INFORMATION to inode. */
1N/A if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0,
1N/A (u8*)si, si_len)) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to add STANDARD_INFORMATION "
1N/A "attribute.\n");
1N/A goto err_out;
1N/A }
1N/A /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
1N/A /*
1N/A * Calculate security descriptor length. We have 2 sub-authorities in
1N/A * owner and group SIDs, but structure SID contain only one, so add
1N/A * 4 bytes to every SID.
1N/A */
1N/A sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
1N/A sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
1N/A sd = calloc(1, sd_len);
1N/A if (!sd) {
1N/A err = errno;
1N/A ntfs_log_error("Not enough memory.\n");
1N/A goto err_out;
1N/A }
1N/A sd->revision = 1;
1N/A sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
1N/A sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
1N/A sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
1N/A sid->revision = 1;
1N/A sid->sub_authority_count = 2;
1N/A sid->sub_authority[0] = cpu_to_le32(32);
1N/A sid->sub_authority[1] = cpu_to_le32(544);
1N/A sid->identifier_authority.value[5] = 5;
1N/A sid = (SID*)((u8*)sid + sizeof(SID) + 4);
1N/A sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
1N/A sid->revision = 1;
1N/A sid->sub_authority_count = 2;
1N/A sid->sub_authority[0] = cpu_to_le32(32);
1N/A sid->sub_authority[1] = cpu_to_le32(544);
1N/A sid->identifier_authority.value[5] = 5;
1N/A acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
1N/A sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
1N/A acl->revision = 2;
1N/A acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
1N/A acl->ace_count = cpu_to_le16(1);
1N/A ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
1N/A ace->type = ACCESS_ALLOWED_ACE_TYPE;
1N/A ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
1N/A ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
1N/A ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */
1N/A ace->sid.revision = 1;
1N/A ace->sid.sub_authority_count = 1;
1N/A ace->sid.sub_authority[0] = 0;
1N/A ace->sid.identifier_authority.value[5] = 1;
1N/A /* Add SECURITY_DESCRIPTOR attribute to inode. */
1N/A if (ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0,
1N/A (u8*)sd, sd_len)) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to add SECURITY_DESCRIPTOR "
1N/A "attribute.\n");
1N/A goto err_out;
1N/A }
1N/A rollback_sd = 1;
1N/A /* Add DATA/INDEX_ROOT attribute. */
1N/A if (S_ISDIR(type)) {
1N/A INDEX_ROOT *ir = NULL;
1N/A INDEX_ENTRY *ie;
1N/A int ir_len, index_len;
1N/A
1N/A /* Create INDEX_ROOT attribute. */
1N/A index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER);
1N/A ir_len = offsetof(INDEX_ROOT, index) + index_len;
1N/A ir = calloc(1, ir_len);
1N/A if (!ir) {
1N/A err = errno;
1N/A ntfs_log_error("Not enough memory.\n");
1N/A goto err_out;
1N/A }
1N/A ir->type = AT_FILE_NAME;
1N/A ir->collation_rule = COLLATION_FILE_NAME;
1N/A ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size);
1N/A if (ni->vol->cluster_size <= ni->vol->indx_record_size)
1N/A ir->clusters_per_index_block =
1N/A ni->vol->indx_record_size >>
1N/A ni->vol->cluster_size_bits;
1N/A else
1N/A ir->clusters_per_index_block =
1N/A ni->vol->indx_record_size >>
1N/A ni->vol->sector_size_bits;
1N/A ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER));
1N/A ir->index.index_length = cpu_to_le32(index_len);
1N/A ir->index.allocated_size = cpu_to_le32(index_len);
1N/A ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT));
1N/A ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER));
1N/A ie->key_length = 0;
1N/A ie->flags = INDEX_ENTRY_END;
1N/A /* Add INDEX_ROOT attribute to inode. */
1N/A if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4,
1N/A (u8*)ir, ir_len)) {
1N/A err = errno;
1N/A free(ir);
1N/A ntfs_log_error("Failed to add INDEX_ROOT attribute.\n");
1N/A goto err_out;
1N/A }
1N/A free(ir);
1N/A } else {
1N/A INTX_FILE *data;
1N/A int data_len;
1N/A
1N/A switch (type) {
1N/A case S_IFBLK:
1N/A case S_IFCHR:
1N/A data_len = offsetof(INTX_FILE, u.s.device_end);
1N/A data = ntfs_malloc(data_len);
1N/A if (!data) {
1N/A err = errno;
1N/A goto err_out;
1N/A }
1N/A data->u.s.major = cpu_to_le64(major(dev));
1N/A data->u.s.minor = cpu_to_le64(minor(dev));
1N/A if (type == S_IFBLK)
1N/A data->magic = INTX_BLOCK_DEVICE;
1N/A if (type == S_IFCHR)
1N/A data->magic = INTX_CHARACTER_DEVICE;
1N/A break;
1N/A case S_IFLNK:
1N/A data_len = sizeof(INTX_FILE_TYPES) +
1N/A target_len * sizeof(ntfschar);
1N/A data = ntfs_malloc(data_len);
1N/A if (!data) {
1N/A err = errno;
1N/A goto err_out;
1N/A }
1N/A data->magic = INTX_SYMBOLIC_LINK;
1N/A memcpy(data->u.target, target,
1N/A target_len * sizeof(ntfschar));
1N/A break;
1N/A case S_IFSOCK:
1N/A data = NULL;
1N/A data_len = 1;
1N/A break;
1N/A default: /* FIFO or regular file. */
1N/A data = NULL;
1N/A data_len = 0;
1N/A break;
1N/A }
1N/A /* Add DATA attribute to inode. */
1N/A if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data,
1N/A data_len)) {
1N/A err = errno;
1N/A free(data);
1N/A ntfs_log_error("Failed to add DATA attribute.\n");
1N/A goto err_out;
1N/A }
1N/A rollback_data = 1;
1N/A free(data);
1N/A }
1N/A /* Create FILE_NAME attribute. */
1N/A fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
1N/A fn = ntfs_calloc(fn_len);
1N/A if (!fn) {
1N/A err = errno;
1N/A goto err_out;
1N/A }
1N/A fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
1N/A le16_to_cpu(dir_ni->mrec->sequence_number));
1N/A fn->file_name_length = name_len;
1N/A fn->file_name_type = FILE_NAME_POSIX;
1N/A if (S_ISDIR(type))
1N/A fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT;
1N/A if (!S_ISREG(type) && !S_ISDIR(type))
1N/A fn->file_attributes = FILE_ATTR_SYSTEM;
1N/A fn->creation_time = utc2ntfs(ni->creation_time);
1N/A fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
1N/A fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
1N/A fn->last_access_time = utc2ntfs(ni->last_access_time);
1N/A memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
1N/A /* Add FILE_NAME attribute to inode. */
1N/A if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to add FILE_NAME attribute.\n");
1N/A goto err_out;
1N/A }
1N/A /* Add FILE_NAME attribute to index. */
1N/A if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
1N/A le16_to_cpu(ni->mrec->sequence_number)))) {
1N/A err = errno;
1N/A ntfs_log_perror("Failed to add entry to the index");
1N/A goto err_out;
1N/A }
1N/A /* Set hard links count and directory flag. */
1N/A ni->mrec->link_count = cpu_to_le16(1);
1N/A if (S_ISDIR(type))
1N/A ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY;
1N/A ntfs_inode_mark_dirty(ni);
1N/A /* Done! */
1N/A free(fn);
1N/A free(si);
1N/A free(sd);
1N/A ntfs_log_trace("Done.\n");
1N/A return ni;
1N/Aerr_out:
1N/A ntfs_log_trace("Failed.\n");
1N/A if (rollback_sd) {
1N/A ntfs_attr *na;
1N/A
1N/A na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
1N/A if (!na)
1N/A ntfs_log_perror("Failed to open SD (0x50) attribute of "
1N/A " inode 0x%llx. Run chkdsk.\n",
1N/A (unsigned long long)ni->mft_no);
1N/A else if (ntfs_attr_rm(na))
1N/A ntfs_log_perror("Failed to remove SD (0x50) attribute "
1N/A "of inode 0x%llx. Run chkdsk.\n",
1N/A (unsigned long long)ni->mft_no);
1N/A }
1N/A if (rollback_data) {
1N/A ntfs_attr *na;
1N/A
1N/A na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
1N/A if (!na)
1N/A ntfs_log_perror("Failed to open data attribute of "
1N/A " inode 0x%llx. Run chkdsk.\n",
1N/A (unsigned long long)ni->mft_no);
1N/A else if (ntfs_attr_rm(na))
1N/A ntfs_log_perror("Failed to remove data attribute of "
1N/A "inode 0x%llx. Run chkdsk.\n",
1N/A (unsigned long long)ni->mft_no);
1N/A }
1N/A /*
1N/A * Free extent MFT records (should not exist any with current
1N/A * ntfs_create implementation, but for any case if something will be
1N/A * changed in the future).
1N/A */
1N/A while (ni->nr_extents)
1N/A if (ntfs_mft_record_free(ni->vol, *(ni->u.extent_nis))) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to free extent MFT record. "
1N/A "Leaving inconsistent metadata.\n");
1N/A }
1N/A if (ntfs_mft_record_free(ni->vol, ni))
1N/A ntfs_log_error("Failed to free MFT record. "
1N/A "Leaving inconsistent metadata. Run chkdsk.\n");
1N/A free(fn);
1N/A free(si);
1N/A free(sd);
1N/A errno = err;
1N/A return NULL;
1N/A}
1N/A
1N/A/**
1N/A * Some wrappers around __ntfs_create() ...
1N/A */
1N/A
1N/Antfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
1N/A dev_t type)
1N/A{
1N/A if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO &&
1N/A type != S_IFSOCK) {
1N/A ntfs_log_error("Invalid arguments.\n");
1N/A return NULL;
1N/A }
1N/A return __ntfs_create(dir_ni, name, name_len, type, 0, NULL, 0);
1N/A}
1N/A
1N/Antfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
1N/A dev_t type, dev_t dev)
1N/A{
1N/A if (type != S_IFCHR && type != S_IFBLK) {
1N/A ntfs_log_error("Invalid arguments.\n");
1N/A return NULL;
1N/A }
1N/A return __ntfs_create(dir_ni, name, name_len, type, dev, NULL, 0);
1N/A}
1N/A
1N/Antfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
1N/A ntfschar *target, u8 target_len)
1N/A{
1N/A if (!target || !target_len) {
1N/A ntfs_log_error("Invalid arguments.\n");
1N/A return NULL;
1N/A }
1N/A return __ntfs_create(dir_ni, name, name_len, S_IFLNK, 0,
1N/A target, target_len);
1N/A}
1N/A
1N/A/**
1N/A * ntfs_delete - delete file or directory from ntfs volume
1N/A * @pni: ntfs inode for object to delete
1N/A * @dir_ni: ntfs inode for directory in which delete object
1N/A * @name: unicode name of the object to delete
1N/A * @name_len: length of the name in unicode characters
1N/A *
1N/A * @pni is pointer to pointer to ntfs_inode structure. Upon successful
1N/A * completion and if inode is really deleted (there are no more links left to
1N/A * it) this function will close @*pni and set it to NULL, in the other cases
1N/A * @*pni will stay opened.
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_delete(ntfs_inode **pni, ntfs_inode *dir_ni, ntfschar *name,
1N/A u8 name_len)
1N/A{
1N/A ntfs_attr_search_ctx *actx = NULL;
1N/A ntfs_index_context *ictx = NULL;
1N/A ntfs_inode *ni;
1N/A FILE_NAME_ATTR *fn = NULL;
1N/A BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
1N/A BOOL case_sensitive_match = TRUE;
1N/A int err = 0;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A if (!pni || !(ni = *pni) || !dir_ni || !name || !name_len ||
1N/A ni->nr_extents == -1 || dir_ni->nr_extents == -1) {
1N/A ntfs_log_error("Invalid arguments.\n");
1N/A errno = EINVAL;
1N/A goto err_out;
1N/A }
1N/A if (ni->nr_references > 1 && le16_to_cpu(ni->mrec->link_count) == 1) {
1N/A ntfs_log_error("Trying to deleting inode with left "
1N/A "references.\n");
1N/A errno = EINVAL;
1N/A goto err_out;
1N/A }
1N/A /*
1N/A * Search for FILE_NAME attribute with such name. If it's in POSIX or
1N/A * WIN32_AND_DOS namespace, then simply remove it from index and inode.
1N/A * If filename in DOS or in WIN32 namespace, then remove DOS name first,
1N/A * only then remove WIN32 name. Mark WIN32 name as POSIX name to prevent
1N/A * chkdsk to complain about DOS name absence in case if DOS name had
1N/A * been successfully deleted, but WIN32 name remove failed.
1N/A */
1N/A actx = ntfs_attr_get_search_ctx(ni, NULL);
1N/A if (!actx)
1N/A goto err_out;
1N/Asearch:
1N/A while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
1N/A 0, NULL, 0, actx)) {
1N/A errno = 0;
1N/A fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
1N/A le16_to_cpu(actx->attr->u.res.value_offset));
1N/A ntfs_log_trace("Found filename with instance number %d.\n",
1N/A le16_to_cpu(actx->attr->instance));
1N/A if (looking_for_dos_name) {
1N/A if (fn->file_name_type == FILE_NAME_DOS)
1N/A break;
1N/A else
1N/A continue;
1N/A }
1N/A if (looking_for_win32_name) {
1N/A if (fn->file_name_type == FILE_NAME_WIN32)
1N/A break;
1N/A else
1N/A continue;
1N/A }
1N/A if (dir_ni->mft_no == MREF_LE(fn->parent_directory) &&
1N/A ntfs_names_are_equal(fn->file_name,
1N/A fn->file_name_length, name,
1N/A name_len, case_sensitive_match ?
1N/A CASE_SENSITIVE : IGNORE_CASE, ni->vol->upcase,
1N/A ni->vol->upcase_len)) {
1N/A if (fn->file_name_type == FILE_NAME_WIN32) {
1N/A looking_for_dos_name = TRUE;
1N/A ntfs_attr_reinit_search_ctx(actx);
1N/A ntfs_log_trace("Restart search. "
1N/A "Looking for DOS name.\n");
1N/A continue;
1N/A }
1N/A if (fn->file_name_type == FILE_NAME_DOS)
1N/A looking_for_dos_name = TRUE;
1N/A break;
1N/A }
1N/A }
1N/A if (errno) {
1N/A /*
1N/A * If case sensitive search failed and volume mounted case
1N/A * insensitive, then try once again ignoring case.
1N/A */
1N/A if (errno == ENOENT && !NVolCaseSensitive(ni->vol) &&
1N/A case_sensitive_match) {
1N/A case_sensitive_match = FALSE;
1N/A ntfs_attr_reinit_search_ctx(actx);
1N/A ntfs_log_trace("Restart search. Ignore case.");
1N/A goto search;
1N/A }
1N/A ntfs_log_error("Failed to find requested filename in FILE_NAME "
1N/A "attributes that belong to this inode.\n");
1N/A goto err_out;
1N/A }
1N/A /* If deleting directory check it to be empty. */
1N/A if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1N/A ntfs_attr *na;
1N/A
1N/A na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4);
1N/A if (!na) {
1N/A ntfs_log_error("Corrupt directory or library bug.\n");
1N/A errno = EIO;
1N/A goto err_out;
1N/A }
1N/A /*
1N/A * Do not allow non-empty directory deletion if hard links count
1N/A * is 1 (always) or 2 (in case if filename in DOS namespace,
1N/A * because we delete it first in file which have both WIN32 and
1N/A * DOS names).
1N/A */
1N/A if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(
1N/A INDEX_ENTRY_HEADER)) && (le16_to_cpu(
1N/A ni->mrec->link_count) == 1 ||
1N/A (le16_to_cpu(ni->mrec->link_count) == 2 &&
1N/A fn->file_name_type == FILE_NAME_DOS))) {
1N/A ntfs_attr_close(na);
1N/A ntfs_log_error("Directory is not empty.\n");
1N/A errno = ENOTEMPTY;
1N/A goto err_out;
1N/A }
1N/A ntfs_attr_close(na);
1N/A }
1N/A /* One more sanity check. */
1N/A if (ni->nr_references > 1 && looking_for_dos_name &&
1N/A le16_to_cpu(ni->mrec->link_count) == 2) {
1N/A ntfs_log_error("Trying to deleting inode with left "
1N/A "references.\n");
1N/A errno = EINVAL;
1N/A goto err_out;
1N/A }
1N/A ntfs_log_trace("Found!\n");
1N/A /* Search for such FILE_NAME in index. */
1N/A ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
1N/A if (!ictx)
1N/A goto err_out;
1N/A if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->u.res.value_length), ictx))
1N/A goto err_out;
1N/A /* Set namespace to POSIX for WIN32 name. */
1N/A if (fn->file_name_type == FILE_NAME_WIN32) {
1N/A fn->file_name_type = FILE_NAME_POSIX;
1N/A ntfs_inode_mark_dirty(actx->ntfs_ino);
1N/A ((FILE_NAME_ATTR*)ictx->data)->file_name_type = FILE_NAME_POSIX;
1N/A ntfs_index_entry_mark_dirty(ictx);
1N/A }
1N/A /* Do not support reparse point deletion yet. */
1N/A if (((FILE_NAME_ATTR*)ictx->data)->file_attributes &
1N/A FILE_ATTR_REPARSE_POINT) {
1N/A errno = EOPNOTSUPP;
1N/A goto err_out;
1N/A }
1N/A /* Remove FILE_NAME from index. */
1N/A if (ntfs_index_rm(ictx))
1N/A goto err_out;
1N/A
1N/A /* Remove FILE_NAME from inode. */
1N/A if (ntfs_attr_record_rm(actx))
1N/A goto err_out;
1N/A /* Decrement hard link count. */
1N/A ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
1N/A ni->mrec->link_count) - 1);
1N/A ntfs_inode_mark_dirty(ni);
1N/A if (looking_for_dos_name) {
1N/A looking_for_dos_name = FALSE;
1N/A looking_for_win32_name = TRUE;
1N/A ntfs_attr_reinit_search_ctx(actx);
1N/A ntfs_log_trace("DOS name deleted. "
1N/A "Now search for WIN32 name.\n");
1N/A goto search;
1N/A } else
1N/A ntfs_log_trace("Deleted.\n");
1N/A /* TODO: Update object id, quota and security indexes if required. */
1N/A /*
1N/A * If hard link count is not equal to zero then we are done. In other
1N/A * case there are no reference to this inode left, so we should free all
1N/A * non-resident attributes and mark all MFT record as not in use.
1N/A */
1N/A if (ni->mrec->link_count)
1N/A goto out;
1N/A ntfs_attr_reinit_search_ctx(actx);
1N/A while (!ntfs_attrs_walk(actx)) {
1N/A if (actx->attr->non_resident) {
1N/A runlist *rl;
1N/A
1N/A rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr,
1N/A NULL);
1N/A if (!rl) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to decompress runlist. "
1N/A "Leaving inconsistent "
1N/A "metadata.\n");
1N/A continue;
1N/A }
1N/A if (ntfs_cluster_free_from_rl(ni->vol, rl)) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to free clusters. "
1N/A "Leaving inconsistent "
1N/A "metadata.\n");
1N/A continue;
1N/A }
1N/A free(rl);
1N/A }
1N/A }
1N/A if (errno != ENOENT) {
1N/A err = errno;
1N/A ntfs_log_error("Attribute enumeration failed. "
1N/A "Probably leaving inconsistent metadata.\n");
1N/A }
1N/A /* All extents should be attached after attribute walk. */
1N/A while (ni->nr_extents)
1N/A if (ntfs_mft_record_free(ni->vol, *(ni->u.extent_nis))) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to free extent MFT record. "
1N/A "Leaving inconsistent metadata.\n");
1N/A }
1N/A if (ntfs_mft_record_free(ni->vol, ni)) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to free base MFT record. "
1N/A "Leaving inconsistent metadata.\n");
1N/A }
1N/A *pni = NULL;
1N/Aout:
1N/A if (actx)
1N/A ntfs_attr_put_search_ctx(actx);
1N/A if (ictx)
1N/A ntfs_index_ctx_put(ictx);
1N/A if (err) {
1N/A ntfs_log_error("%s(): Failed.\n", "ntfs_delete");
1N/A errno = err;
1N/A return -1;
1N/A }
1N/A ntfs_log_trace("Done.\n");
1N/A return 0;
1N/Aerr_out:
1N/A err = errno;
1N/A goto out;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_link - create hard link for file or directory
1N/A * @ni: ntfs inode for object to create hard link
1N/A * @dir_ni: ntfs inode for directory in which new link should be placed
1N/A * @name: unicode name of the new link
1N/A * @name_len: length of the name in unicode characters
1N/A *
1N/A * NOTE: At present we allow creating hard links to directories, we use them
1N/A * in a temporary state during rename. But it's definitely bad idea to have
1N/A * hard links to directories as a result of operation.
1N/A * FIXME: Create internal __ntfs_link that allows hard links to a directories
1N/A * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link.
1N/A *
1N/A * Return 0 on success or -1 on error with errno set to the error code.
1N/A */
1N/Aint ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
1N/A{
1N/A FILE_NAME_ATTR *fn = NULL;
1N/A int fn_len, err;
1N/A
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A if (!ni || !dir_ni || !name || !name_len ||
1N/A ni->mft_no == dir_ni->mft_no) {
1N/A err = EINVAL;
1N/A ntfs_log_error("Invalid arguments.");
1N/A goto err_out;
1N/A }
1N/A /* FIXME: Reparse points requires special handling. */
1N/A if (ni->flags & FILE_ATTR_REPARSE_POINT) {
1N/A err = EOPNOTSUPP;
1N/A goto err_out;
1N/A }
1N/A /* Create FILE_NAME attribute. */
1N/A fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
1N/A fn = ntfs_calloc(fn_len);
1N/A if (!fn) {
1N/A err = errno;
1N/A goto err_out;
1N/A }
1N/A fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
1N/A le16_to_cpu(dir_ni->mrec->sequence_number));
1N/A fn->file_name_length = name_len;
1N/A fn->file_name_type = FILE_NAME_POSIX;
1N/A fn->file_attributes = ni->flags;
1N/A if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
1N/A fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT;
1N/A fn->allocated_size = cpu_to_sle64(ni->allocated_size);
1N/A fn->data_size = cpu_to_sle64(ni->data_size);
1N/A fn->creation_time = utc2ntfs(ni->creation_time);
1N/A fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
1N/A fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
1N/A fn->last_access_time = utc2ntfs(ni->last_access_time);
1N/A memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
1N/A /* Add FILE_NAME attribute to index. */
1N/A if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
1N/A le16_to_cpu(ni->mrec->sequence_number)))) {
1N/A err = errno;
1N/A ntfs_log_error("Failed to add entry to the index.\n");
1N/A goto err_out;
1N/A }
1N/A /* Add FILE_NAME attribute to inode. */
1N/A if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
1N/A ntfs_index_context *ictx;
1N/A
1N/A err = errno;
1N/A ntfs_log_error("Failed to add FILE_NAME attribute.\n");
1N/A /* Try to remove just added attribute from index. */
1N/A ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
1N/A if (!ictx)
1N/A goto rollback_failed;
1N/A if (ntfs_index_lookup(fn, fn_len, ictx)) {
1N/A ntfs_index_ctx_put(ictx);
1N/A goto rollback_failed;
1N/A }
1N/A if (ntfs_index_rm(ictx)) {
1N/A ntfs_index_ctx_put(ictx);
1N/A goto rollback_failed;
1N/A }
1N/A goto err_out;
1N/A }
1N/A /* Increment hard links count. */
1N/A ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
1N/A ni->mrec->link_count) + 1);
1N/A /* Done! */
1N/A ntfs_inode_mark_dirty(ni);
1N/A free(fn);
1N/A ntfs_log_trace("Done.\n");
1N/A return 0;
1N/Arollback_failed:
1N/A ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n");
1N/Aerr_out:
1N/A ntfs_log_error("%s(): Failed.\n", "ntfs_link");
1N/A free(fn);
1N/A errno = err;
1N/A return -1;
1N/A}
1N/A