Changes by: antona
Update of /cvsroot/linux-ntfs/linux-ntfs/libntfs
In directory usw-pr-cvs1:/tmp/cvs-serv13301/libntfs
Modified Files:
bootsect.c dir.c
Log Message:
The beginning of the directory operations! Introduce dir.[hc] and ntfs_lookup_inode_by_name().
Index: bootsect.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/bootsect.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -U2 -r1.10 -r1.11
--- bootsect.c 2 Jun 2002 23:02:20 -0000 1.10
+++ bootsect.c 6 Jun 2002 20:47:33 -0000 1.11
@@ -186,4 +186,8 @@
errno = EINVAL;
+ vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
+ vol->sector_size_bits = ffs(vol->sector_size) - 1;
+ Dprintf("SectorSize = 0x%x\n", vol->sector_size);
+ Dprintf("SectorSizeBits = %u\n", vol->sector_size_bits);
/*
* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
@@ -214,6 +218,5 @@
return -1;
}
- vol->cluster_size = sectors_per_cluster *
- le16_to_cpu(bs->bpb.bytes_per_sector);
+ vol->cluster_size = sectors_per_cluster * vol->sector_size;
if (vol->cluster_size & (vol->cluster_size - 1)) {
Dprintf("Error: %s is not a valid NTFS partition! "
Index: dir.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/dir.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -U2 -r1.1 -r1.2
--- dir.c 6 Jun 2002 15:41:30 -0000 1.1
+++ dir.c 6 Jun 2002 20:47:33 -0000 1.2
@@ -22,5 +22,7 @@
*/
+#include <stdlib.h>
#include <errno.h>
+#include <string.h>
#include "types.h"
@@ -29,9 +31,10 @@
#include "inode.h"
#include "dir.h"
+#include "volume.h"
/**
* The little endian Unicode string $I30 as a global constant.
*/
-const uchar_t I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'),
+uchar_t 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) };
@@ -42,5 +45,4 @@
* @uname: Unicode name for which to search in the directory
* @uname_len: length of the name @uname in Unicode characters
- * @res: return the found file name if necessary (see below)
*
* Look for an inode with name @uname in the directory with inode @dir_ni.
@@ -50,9 +52,6 @@
* is a 64-bit number containing the sequence number.
*
- * On error, a negative value is returned corresponding to the error code. In
- * particular if the inode is not found -ENOENT is returned. Note that you
- * can't just check the return value for being negative, you have to check the
- * inode number for being negative which you can extract using MREC(return
- * value).
+ * 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.
@@ -61,16 +60,395 @@
* 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 case insensitive match and setup @res (which we allocate!) with the mft
- * reference, the file name type, length and with a copy of the little endian
- * Unicode file name itself. If we match a file name which is in the DOS name
- * space, we only return the mft reference and file name type in @res.
- * ntfs_lookup() then uses this to find the long file name in the inode itself.
- * This is to avoid polluting the dcache with short file names. We want them to
- * work but we don't care for how quickly one can access them. This also fixes
- * the dcache aliasing issues.
+ * 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_lookup_inode_by_name(ntfs_inode *dir_ni, const uchar_t *uname,
+__u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const uchar_t *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_get_attr_search_ctx(dir_ni, NULL);
+ if (!ctx)
+ return -1;
+
+ /* Find the index root attribute in the mft record. */
+ if (!ntfs_lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0, NULL,
+ 0, ctx)) {
+ Dprintf("Index root attribute missing in directory inode "
+ "0x%Lx: %s\n",
+ (unsigned long long)dir_ni->mft_no,
+ strerror(errno));
+ goto put_err_out;
+ }
+ /* Get to the index root value (it's been verified in read_inode). */
+ 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_SECTOR_SIZE ||
+ index_block_size & (index_block_size - 1)) {
+ Dprintf("Index block size %u is invalid.\n", 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_are_names_equal(uname, uname_len,
+ (uchar_t*)&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_put_attr_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_are_names_equal(uname, uname_len,
+ (uchar_t*)&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) {
+ Dputs("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 "
+ "lin...@li....");
+ 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_collate_names(uname, uname_len,
+ (uchar_t*)&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_collate_names(uname, uname_len,
+ (uchar_t*)&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_put_attr_search_ctx(ctx);
+ if (mref)
+ return mref;
+ Dputs("Entry not found.");
+ 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, I30, 4);
+ if (!ia_na) {
+ Dprintf("Failed to open index allocation attribute. Directory "
+ "inode 0x%Lx is corrupt or driver bug: %s\n",
+ (unsigned long long)dir_ni->mft_no,
+ strerror(errno));
+ goto put_err_out;
+ }
+
+ /* Allocate a buffer for the current index block. */
+ ia = (INDEX_ALLOCATION*)malloc(index_block_size);
+ if (!ia) {
+ Dperror("Failed to allocate buffer for index block");
+
+ 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;
+ Dprintf("Failed to read vcn 0x%Lx: %s\n", vcn, strerror(errno));
+ goto close_err_out;
+ }
+
+ if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
+ Dprintf("Actual VCN (0x%Lx) of index buffer is different from "
+ "expected VCN (0x%Lx).\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) {
+ Dprintf("Index buffer (VCN 0x%Lx) of directory inode 0x%Lx "
+ "has a size (%u) differing from the directory "
+ "specified size (%u). Directory inode is "
+ "corrupt or driver bug.\n", (long long)vcn,
+ (unsigned long long)dir_ni->mft_no,
+ le32_to_cpu(ia->index.allocated_size) + 0x18,
+ 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) {
+ Dprintf("Size of index buffer (VCN 0x%Lx) of directory inode "
+ "0x%Lx 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) {
+ Dprintf("Index entry out of bounds in directory inode "
+ "0x%Lx.\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_are_names_equal(uname, uname_len,
+ (uchar_t*)&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);
+ ntfs_attr_close(ia_na);
+ ntfs_put_attr_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_are_names_equal(uname, uname_len,
+ (uchar_t*)&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) {
+ Dputs("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 "
+ "lin...@li....");
+ 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_collate_names(uname, uname_len,
+ (uchar_t*)&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_collate_names(uname, uname_len,
+ (uchar_t*)&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) {
+ Dprintf("Index entry with child node found in a leaf "
+ "node in directory inode 0x%Lx.\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;
+ Dprintf("Negative child node vcn in directory inode 0x%Lx.\n",
+ (unsigned long long)dir_ni->mft_no);
+ errno = EIO;
+ goto close_err_out;
+ }
+ ntfs_attr_close(ia_na);
+ ntfs_put_attr_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;
+ Dputs("Entry not found.");
+ errno = ENOENT;
+ return -1;
+put_err_out:
+ eo = EIO;
+ Dputs("Corrupt directory. Aborting lookup.");
+eo_put_err_out:
+ ntfs_put_attr_search_ctx(ctx);
+ errno = eo;
+ return -1;
+close_err_out:
+ eo = errno;
+ free(ia);
+ ntfs_attr_close(ia_na);
+ goto eo_put_err_out;
}
|