/**
* dir.c - Directory handling code. Part of the Linux-NTFS project.
*
* Copyright (c) 2002-2005 Anton Altaparmakov
* Copyright (c) 2005-2007 Yura Pakhuchiy
* Copyright (c) 2004-2005 Richard Russon
*
* 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.
*
* 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
*/
#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
#endif
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#include "compat.h"
#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.
*/
const_cpu_to_le16('\0') };
const_cpu_to_le16('\0') };
const_cpu_to_le16('\0') };
const_cpu_to_le16('\0') };
const_cpu_to_le16('\0') };
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.
*/
const int uname_len)
{
return -1;
}
if (!ctx)
return -1;
/* Find the index root attribute in the mft record. */
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. */
if (index_block_size < NTFS_BLOCK_SIZE ||
ntfs_log_debug("Index block size %u is invalid.\n",
(unsigned)index_block_size);
goto put_err_out;
}
/* The first index entry. */
/*
* Loop until we exceed valid memory (corruption case) or until we
* reach the last entry.
*/
/* Bounds checks. */
sizeof(INDEX_ENTRY_HEADER) > 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.
*/
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.
*/
/*
* We have a perfect match, so we don't need to care
* about having matched imperfectly before.
*/
return mref;
}
/*
* For a case insensitive mount, we also perform a case
* insensitive comparison. If the comparison matches, we cache
* the mft reference in mref. Use first case insensitive match
* in case if no name matches case sensitive, but several names
* matches case insensitive.
*/
/*
* Not a perfect match, need to do full blown collation so we
* know which way in the B+tree we have to go.
*/
/*
* 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.
*/
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 (mref)
return mref;
ntfs_log_debug("Entry not found.\n");
return -1;
} /* Child node present, descend into it. */
/* Open the index allocation attribute. */
if (!ia_na) {
ntfs_log_perror("Failed to open index allocation attribute. "
"Directory inode 0x%llx is corrupt or driver "
goto put_err_out;
}
/* Allocate a buffer for the current index block. */
if (!ia) {
ntfs_log_perror("Failed to allocate buffer for index block");
goto put_err_out;
}
/* Determine the size of a vcn in the directory index. */
} else {
}
/* Get the starting vcn of the index_block holding the child node. */
/* Read the index block starting at vcn. */
if (br != 1) {
if (br != -1)
ntfs_log_perror("Failed to read vcn 0x%llx",
(unsigned long long)vcn);
goto close_err_out;
}
ntfs_log_debug("Actual VCN (0x%llx) of index buffer is "
"different from expected VCN (0x%llx).\n",
(long long)vcn);
goto close_err_out;
}
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",
allocated_size) + 0x18, (unsigned)
goto close_err_out;
}
ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory "
"inode 0x%llx exceeds maximum size.\n",
mft_no);
goto close_err_out;
}
/* The first index entry. */
/*
* 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.
*/
/* Bounds check. */
sizeof(INDEX_ENTRY_HEADER) > index_end ||
index_end) {
ntfs_log_debug("Index entry out of bounds in directory "
"inode 0x%llx.\n",
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.
*/
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.
*/
/*
* We have a perfect match, so we don't need to care
* about having matched imperfectly before.
*/
return mref;
}
/*
* For a case insensitive mount, we also perform a case
* insensitive comparison. If the comparison matches, we cache
* the mft reference in mref. Use first case insensitive match
* in case if no name matches case sensitive, but several names
* matches case insensitive.
*/
/*
* Not a perfect match, need to do full blown collation so we
* know which way in the B+tree we have to go.
*/
/*
* 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.
*/
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.
*/
ntfs_log_debug("Index entry with child node found in a "
"leaf node in directory inode "
"0x%llx.\n",
goto close_err_out;
}
/* Child node present, descend into it. */
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);
goto close_err_out;
}
/*
* 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");
return -1;
ntfs_log_debug("Corrupt directory. Aborting lookup.\n");
return -1;
goto eo_put_err_out;
}
/**
* ntfs_pathname_to_inode_num - find the inode number 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: -1 Error, the pathname was invalid, or some other error occurred
* else Success, the pathname was valid
*/
const char *pathname)
{
char *p, *q;
goto close;
}
if (parent) {
} else
ntfs_log_error("Out of memory.\n");
goto close;
}
p = ascii;
/* Remove leading /'s. */
while (p && *p == PATH_SEP)
p++;
while (p && *p) {
if (!ni) {
if (!ni) {
ntfs_log_debug("Cannot open inode %llu.\n",
(unsigned long long)inum);
goto close;
}
}
/* Find the end of the first token. */
if (q != NULL) {
*q = 0;
q++;
}
if (len < 0) {
ntfs_log_debug("Couldn't convert name to Unicode: "
"%s.\n", p);
goto close;
}
ntfs_log_debug("Couldn't find name '%s' in pathname "
"'%s'.\n", p, pathname);
goto close;
}
p = q;
while (p && *p == PATH_SEP)
p++;
}
if (err)
return result;
}
/**
* 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
*/
const char *pathname)
{
return NULL;
}
/*
* The little endian Unicode string ".." for ntfs_readdir().
*/
const_cpu_to_le16('.'),
const_cpu_to_le16('\0') };
/**
* ntfs_filldir - ntfs specific filldir method
* @vol: ntfs volume with wjich we are working
* @pos: current position in directory
* @ie: current index entry
* @dirent: context for filldir callback supplied by the caller
* @filldir: filldir callback supplied by the caller
*
* Pass information specifying the current directory entry @ie to the @filldir
* callback.
*/
{
unsigned dt_type;
ntfs_log_trace("Entering.\n");
/* Skip root directory self reference entry. */
return 0;
else {
else
}
}
/**
* ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode
* @ni: ntfs inode whose parent directory to find
*
* Find the parent directory of the ntfs inode @ni. To do this, find the first
* file name attribute in the mft record of @ni and return the parent mft
* reference from that.
*
* Note this only makes sense for directories, since files can be hard linked
* from multiple directories and there is no way for us to tell which one is
* being looked for.
*
* Technically directories can have hard links, too, but we consider that as
*
* Return the mft reference of the parent directory on success or -1 on error
* with errno set to the error code.
*/
{
int eo;
ntfs_log_trace("Entering.\n");
if (!ni) {
return ERR_MREF(-1);
}
if (!ctx)
return ERR_MREF(-1);
ntfs_log_debug("No file name found in inode 0x%llx. Corrupt "
goto err_out;
}
ntfs_log_debug("File name attribute must be resident. "
"Corrupt inode 0x%llx.\n",
goto io_err_out;
}
ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n",
goto io_err_out;
}
return mref;
return ERR_MREF(-1);
}
/**
* ntfs_readdir - read the contents of an ntfs directory
* @dir_ni: ntfs inode of current directory
* @pos: current position in directory
* @dirent: context for filldir callback supplied by the caller
* @filldir: filldir callback supplied by the caller
*
* Parse the index root and the index blocks that are marked in use in the
* index bitmap and hand each found directory entry to the @filldir callback
* supplied by the caller.
*
* Return 0 on success or -1 on error with errno set to the error code.
*
* Note: Index blocks are parsed in ascending vcn order, from which follows
* that the directory entries are not returned sorted.
*/
{
ntfs_log_trace("Entering.\n");
return -1;
}
return -1;
}
ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n",
/* Open the index allocation attribute. */
if (!ia_na) {
ntfs_log_perror("Failed to open index allocation "
"attribute. Directory inode 0x%llx is "
"corrupt or bug", (unsigned long long)
return -1;
}
i_size = 0;
} else
rc = 0;
/* Are we at end of dir yet? */
goto done;
/* Emulate . and .. for all directories. */
if (!*pos) {
if (rc)
goto err_out;
++*pos;
}
if (*pos == 1) {
ntfs_log_perror("Parent directory not found");
goto dir_err_out;
}
if (rc)
goto err_out;
++*pos;
}
if (!ctx)
goto err_out;
/* Get the offset into the index root attribute. */
/* Find the index root attribute in the mft record. */
ntfs_log_debug("Index root attribute missing in directory "
"inode 0x%llx.\n", (unsigned long long)dir_ni->
mft_no);
goto dir_err_out;
}
/* Get to the index root value. */
/* Determine the size of a vcn in the directory index. */
if (index_block_size < NTFS_BLOCK_SIZE ||
ntfs_log_debug("Index block size %u is invalid.\n",
(unsigned)index_block_size);
goto dir_err_out;
}
} else {
}
/* Are we jumping straight into the index allocation attribute? */
goto skip_index_root;
}
/* The first index entry. */
/*
* Loop until we exceed valid memory (corruption case) or until we
* reach the last entry or until filldir tells us it has had enough
* or signals an error (both covered by the rc test).
*/
ntfs_log_debug("In index root, offset 0x%x.\n",
/* Bounds checks. */
sizeof(INDEX_ENTRY_HEADER) > index_end ||
goto dir_err_out;
/* The last entry cannot contain a name. */
break;
/* Skip index root entry if continuing previous readdir. */
continue;
/* Advance the position even if going to skip the entry. */
/*
* Submit the directory entry to ntfs_filldir(), which will
* invoke the filldir() callback as appropriate.
*/
if (rc)
goto err_out;
}
/* If there is no index allocation attribute we are finished. */
if (!ia_na)
goto EOD;
/* Advance *pos to the beginning of the index allocation. */
if (!ia_na)
goto done;
/* Allocate a buffer for the current index block. */
if (!ia) {
ntfs_log_perror("Failed to allocate buffer for index block");
goto err_out;
}
if (!bmp_na) {
ntfs_log_perror("Failed to open index bitmap attribute");
goto dir_err_out;
}
/* Get the offset into the index allocation attribute. */
ntfs_log_debug("Current index position exceeds index bitmap "
"size.\n");
goto dir_err_out;
}
if (!bmp) {
ntfs_log_perror("Failed to allocate bitmap buffer");
goto err_out;
}
if (br != bmp_buf_size) {
if (br != -1)
ntfs_log_perror("Failed to read from index bitmap attribute");
goto err_out;
}
bmp_buf_pos = 0;
/* If the index block is not in use find the next one that is. */
bmp_pos++;
bmp_buf_pos++;
/* If we have reached the end of the bitmap, we are done. */
goto EOD;
continue;
/* Read next chunk from the index bitmap. */
if (br != bmp_buf_size) {
if (br != -1)
ntfs_log_perror("Failed to read from index bitmap "
"attribute");
goto err_out;
}
}
/* Read the index block starting at bmp_pos. */
if (br != 1) {
if (br != -1)
ntfs_log_perror("Failed to read index block");
goto err_out;
}
ntfs_log_debug("Actual VCN (0x%llx) of index buffer is "
"different from expected VCN (0x%llx) in "
"inode 0x%llx.\n",
(long long)ia_start >> index_vcn_size_bits,
goto dir_err_out;
}
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)ia_start >> index_vcn_size_bits,
+ 0x18, (unsigned)index_block_size);
goto dir_err_out;
}
ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory "
"inode 0x%llx exceeds maximum size.\n",
(long long)ia_start >> index_vcn_size_bits,
goto dir_err_out;
}
/* The first index entry. */
/*
* Loop until we exceed valid memory (corruption case) or until we
* reach the last entry or until ntfs_filldir tells us it has had
* enough or signals an error (both covered by the rc test).
*/
ntfs_log_debug("In index allocation, offset 0x%llx.\n",
/* Bounds checks. */
sizeof(INDEX_ENTRY_HEADER) > index_end ||
index_end) {
ntfs_log_debug("Index entry out of bounds in directory "
"inode 0x%llx.\n", (unsigned long long)
goto dir_err_out;
}
/* The last entry cannot contain a name. */
break;
/* Skip index entry if continuing previous readdir. */
continue;
/* Advance the position even if going to skip the entry. */
/*
* Submit the directory entry to ntfs_filldir(), which will
* invoke the filldir() callback as appropriate.
*/
if (rc)
goto err_out;
}
goto find_next_index_buffer;
EOD:
/* We are finished, set *pos to EOD. */
done:
if (bmp_na)
if (ia_na)
return 0;
if (rc)
(long long)*pos);
ntfs_log_trace("Failed.\n");
if (ctx)
if (bmp_na)
if (ia_na)
return -1;
}
/**
* __ntfs_create - create object on ntfs volume
* @dir_ni: ntfs inode for directory in which create new object
* @name: unicode name of new object
* @name_len: length of the name in unicode characters
* @type: type of the object to create
* @dev: major and minor device numbers (obtained from makedev())
* @target: target in unicode (only for symlinks)
* @target_len: length of target in unicode characters
*
* Internal, use ntfs_create{,_device,_symlink} wrappers instead.
*
* @type can be:
* S_IFREG to create regular file
* S_IFDIR to create directory
* S_IFBLK to create block device
* S_IFCHR to create character device
* S_IFLNK to create symbolic link
* S_IFIFO to create FIFO
* S_IFSOCK to create socket
* other values are invalid.
*
* @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value
* ignored.
*
* @target and @target_len are used only if @type is S_IFLNK, in other cases
* their value ignored.
*
* Return opened ntfs inode that describes created object on success or NULL
* on error with errno set to the error code.
*/
{
ntfs_log_trace("Entering.\n");
/* Sanity checks. */
ntfs_log_error("Invalid arguments.\n");
return NULL;
}
/* FIXME: Reparse points requires special handling. */
errno = EOPNOTSUPP;
return NULL;
}
/* Allocate MFT record for new file. */
if (!ni) {
ntfs_log_error("Failed to allocate new MFT record: %s.\n",
return NULL;
}
/*
* Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION
* version 1.2, windows will upgrade it to version 3 if needed.
*/
if (!si) {
ntfs_log_error("Not enough memory.\n");
goto err_out;
}
}
/* Add STANDARD_INFORMATION to inode. */
ntfs_log_error("Failed to add STANDARD_INFORMATION "
"attribute.\n");
goto err_out;
}
/* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
/*
* Calculate security descriptor length. We have 2 sub-authorities in
* owner and group SIDs, but structure SID contain only one, so add
* 4 bytes to every SID.
*/
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
if (!sd) {
ntfs_log_error("Not enough memory.\n");
goto err_out;
}
/* Add SECURITY_DESCRIPTOR attribute to inode. */
ntfs_log_error("Failed to add SECURITY_DESCRIPTOR "
"attribute.\n");
goto err_out;
}
rollback_sd = 1;
/* Add DATA/INDEX_ROOT attribute. */
/* Create INDEX_ROOT attribute. */
if (!ir) {
ntfs_log_error("Not enough memory.\n");
goto err_out;
}
else
ie->key_length = 0;
/* Add INDEX_ROOT attribute to inode. */
ntfs_log_error("Failed to add INDEX_ROOT attribute.\n");
goto err_out;
}
} else {
int data_len;
switch (type) {
case S_IFBLK:
case S_IFCHR:
if (!data) {
goto err_out;
}
break;
case S_IFLNK:
data_len = sizeof(INTX_FILE_TYPES) +
target_len * sizeof(ntfschar);
if (!data) {
goto err_out;
}
target_len * sizeof(ntfschar));
break;
case S_IFSOCK:
data_len = 1;
break;
default: /* FIFO or regular file. */
data_len = 0;
break;
}
/* Add DATA attribute to inode. */
data_len)) {
ntfs_log_error("Failed to add DATA attribute.\n");
goto err_out;
}
rollback_data = 1;
}
/* Create FILE_NAME attribute. */
if (!fn) {
goto err_out;
}
/* Add FILE_NAME attribute to inode. */
ntfs_log_error("Failed to add FILE_NAME attribute.\n");
goto err_out;
}
/* Add FILE_NAME attribute to index. */
ntfs_log_perror("Failed to add entry to the index");
goto err_out;
}
/* Set hard links count and directory flag. */
/* Done! */
ntfs_log_trace("Done.\n");
return ni;
ntfs_log_trace("Failed.\n");
if (rollback_sd) {
if (!na)
ntfs_log_perror("Failed to open SD (0x50) attribute of "
" inode 0x%llx. Run chkdsk.\n",
else if (ntfs_attr_rm(na))
ntfs_log_perror("Failed to remove SD (0x50) attribute "
"of inode 0x%llx. Run chkdsk.\n",
}
if (rollback_data) {
if (!na)
ntfs_log_perror("Failed to open data attribute of "
" inode 0x%llx. Run chkdsk.\n",
else if (ntfs_attr_rm(na))
ntfs_log_perror("Failed to remove data attribute of "
"inode 0x%llx. Run chkdsk.\n",
}
/*
* Free extent MFT records (should not exist any with current
* ntfs_create implementation, but for any case if something will be
* changed in the future).
*/
while (ni->nr_extents)
ntfs_log_error("Failed to free extent MFT record. "
"Leaving inconsistent metadata.\n");
}
ntfs_log_error("Failed to free MFT record. "
"Leaving inconsistent metadata. Run chkdsk.\n");
return NULL;
}
/**
* Some wrappers around __ntfs_create() ...
*/
{
ntfs_log_error("Invalid arguments.\n");
return NULL;
}
}
{
ntfs_log_error("Invalid arguments.\n");
return NULL;
}
}
{
if (!target || !target_len) {
ntfs_log_error("Invalid arguments.\n");
return NULL;
}
target, target_len);
}
/**
* ntfs_delete - delete file or directory from ntfs volume
* @pni: ntfs inode for object to delete
* @dir_ni: ntfs inode for directory in which delete object
* @name: unicode name of the object to delete
* @name_len: length of the name in unicode characters
*
* @pni is pointer to pointer to ntfs_inode structure. Upon successful
* completion and if inode is really deleted (there are no more links left to
* it) this function will close @*pni and set it to NULL, in the other cases
* @*pni will stay opened.
*
* Return 0 on success or -1 on error with errno set to the error code.
*/
{
int err = 0;
ntfs_log_trace("Entering.\n");
ntfs_log_error("Invalid arguments.\n");
goto err_out;
}
ntfs_log_error("Trying to deleting inode with left "
"references.\n");
goto err_out;
}
/*
* Search for FILE_NAME attribute with such name. If it's in POSIX or
* WIN32_AND_DOS namespace, then simply remove it from index and inode.
* If filename in DOS or in WIN32 namespace, then remove DOS name first,
* only then remove WIN32 name. Mark WIN32 name as POSIX name to prevent
* chkdsk to complain about DOS name absence in case if DOS name had
* been successfully deleted, but WIN32 name remove failed.
*/
if (!actx)
goto err_out;
errno = 0;
ntfs_log_trace("Found filename with instance number %d.\n",
if (looking_for_dos_name) {
break;
else
continue;
}
if (looking_for_win32_name) {
break;
else
continue;
}
ntfs_log_trace("Restart search. "
"Looking for DOS name.\n");
continue;
}
break;
}
}
if (errno) {
/*
* If case sensitive search failed and volume mounted case
* insensitive, then try once again ignoring case.
*/
ntfs_log_trace("Restart search. Ignore case.");
goto search;
}
ntfs_log_error("Failed to find requested filename in FILE_NAME "
"attributes that belong to this inode.\n");
goto err_out;
}
/* If deleting directory check it to be empty. */
if (!na) {
ntfs_log_error("Corrupt directory or library bug.\n");
goto err_out;
}
/*
* Do not allow non-empty directory deletion if hard links count
* is 1 (always) or 2 (in case if filename in DOS namespace,
* because we delete it first in file which have both WIN32 and
* DOS names).
*/
INDEX_ENTRY_HEADER)) && (le16_to_cpu(
ntfs_log_error("Directory is not empty.\n");
goto err_out;
}
}
/* One more sanity check. */
ntfs_log_error("Trying to deleting inode with left "
"references.\n");
goto err_out;
}
ntfs_log_trace("Found!\n");
/* Search for such FILE_NAME in index. */
if (!ictx)
goto err_out;
goto err_out;
/* Set namespace to POSIX for WIN32 name. */
}
/* Do not support reparse point deletion yet. */
errno = EOPNOTSUPP;
goto err_out;
}
/* Remove FILE_NAME from index. */
if (ntfs_index_rm(ictx))
goto err_out;
/* Remove FILE_NAME from inode. */
if (ntfs_attr_record_rm(actx))
goto err_out;
/* Decrement hard link count. */
if (looking_for_dos_name) {
ntfs_log_trace("DOS name deleted. "
"Now search for WIN32 name.\n");
goto search;
} else
ntfs_log_trace("Deleted.\n");
/* TODO: Update object id, quota and security indexes if required. */
/*
* If hard link count is not equal to zero then we are done. In other
* case there are no reference to this inode left, so we should free all
* non-resident attributes and mark all MFT record as not in use.
*/
goto out;
while (!ntfs_attrs_walk(actx)) {
NULL);
if (!rl) {
ntfs_log_error("Failed to decompress runlist. "
"Leaving inconsistent "
"metadata.\n");
continue;
}
ntfs_log_error("Failed to free clusters. "
"Leaving inconsistent "
"metadata.\n");
continue;
}
}
}
ntfs_log_error("Attribute enumeration failed. "
"Probably leaving inconsistent metadata.\n");
}
/* All extents should be attached after attribute walk. */
while (ni->nr_extents)
ntfs_log_error("Failed to free extent MFT record. "
"Leaving inconsistent metadata.\n");
}
ntfs_log_error("Failed to free base MFT record. "
"Leaving inconsistent metadata.\n");
}
out:
if (actx)
if (ictx)
if (err) {
return -1;
}
ntfs_log_trace("Done.\n");
return 0;
goto out;
}
/**
* ntfs_link - create hard link for file or directory
* @ni: ntfs inode for object to create hard link
* @dir_ni: ntfs inode for directory in which new link should be placed
* @name: unicode name of the new link
* @name_len: length of the name in unicode characters
*
* NOTE: At present we allow creating hard links to directories, we use them
* in a temporary state during rename. But it's definitely bad idea to have
* hard links to directories as a result of operation.
* FIXME: Create internal __ntfs_link that allows hard links to a directories
* and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link.
*
* Return 0 on success or -1 on error with errno set to the error code.
*/
{
ntfs_log_trace("Entering.\n");
ntfs_log_error("Invalid arguments.");
goto err_out;
}
/* FIXME: Reparse points requires special handling. */
err = EOPNOTSUPP;
goto err_out;
}
/* Create FILE_NAME attribute. */
if (!fn) {
goto err_out;
}
/* Add FILE_NAME attribute to index. */
ntfs_log_error("Failed to add entry to the index.\n");
goto err_out;
}
/* Add FILE_NAME attribute to inode. */
ntfs_log_error("Failed to add FILE_NAME attribute.\n");
/* Try to remove just added attribute from index. */
if (!ictx)
goto rollback_failed;
goto rollback_failed;
}
if (ntfs_index_rm(ictx)) {
goto rollback_failed;
}
goto err_out;
}
/* Increment hard links count. */
/* Done! */
ntfs_log_trace("Done.\n");
return 0;
ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n");
return -1;
}