Changes by: antona
Update of /cvsroot/linux-ntfs/linux-ntfs/libntfs
In directory usw-pr-cvs1:/tmp/cvs-serv27454/libntfs
Modified Files:
attrib.c dir.c mft.c
Log Message:
ntfs_readdir() has arrived.
Index: attrib.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/attrib.c,v
retrieving revision 1.44
retrieving revision 1.45
diff -U2 -r1.44 -r1.45
--- attrib.c 7 Jun 2002 01:16:20 -0000 1.44
+++ attrib.c 8 Jun 2002 14:12:01 -0000 1.45
@@ -469,5 +469,5 @@
Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x.\n",
- ni->mft_no, type);
+ (unsigned long long)ni->mft_no, type);
if (!ni || !ni->vol || !ni->mrec) {
errno = EINVAL;
@@ -1446,6 +1446,6 @@
Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x, "
- "vcn 0x%Lx.\n", na->ni->mft_no, na->type,
- (long long)vcn);
+ "vcn 0x%Lx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)vcn);
ctx = ntfs_get_attr_search_ctx(na->ni, NULL);
@@ -1677,6 +1677,7 @@
Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x, pos "
- "0x%Lx, count 0x%Lx.\n", na->ni->mft_no, na->type, pos,
- count);
+ "0x%Lx, count 0x%Lx.\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)pos, (long long)count);
if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
errno = EINVAL;
@@ -1854,6 +1855,6 @@
Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr 0x%x, pos "
- "0x%Lx, count 0x%Lx.\n", na->ni->mft_no, na->type, pos,
- count);
+ "0x%Lx, count 0x%Lx.\n", na->ni->mft_no, na->type,
+ (long long)pos, (long long)count);
if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
errno = EINVAL;
@@ -2157,5 +2158,6 @@
Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr type 0x%x, "
- "pos 0x%Lx.\n", na->ni->mft_no, na->type, pos);
+ "pos 0x%Lx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)pos);
if (bk_cnt < 0 || bk_size % NTFS_SECTOR_SIZE) {
errno = EINVAL;
@@ -2208,5 +2210,6 @@
Dprintf(__FUNCTION__ "(): Entering for inode 0x%Lx, attr type 0x%x, "
- "pos 0x%Lx.\n", na->ni->mft_no, na->type, pos);
+ "pos 0x%Lx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)pos);
if (bk_cnt < 0 || bk_size % NTFS_SECTOR_SIZE) {
errno = EINVAL;
Index: dir.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/dir.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -U2 -r1.2 -r1.3
--- dir.c 6 Jun 2002 20:47:33 -0000 1.2
+++ dir.c 8 Jun 2002 14:12:01 -0000 1.3
@@ -37,6 +37,6 @@
*/
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) };
+ const_cpu_to_le16('3'), const_cpu_to_le16('0'),
+ const_cpu_to_le16('\0') };
/**
@@ -100,5 +100,5 @@
goto put_err_out;
}
- /* Get to the index root value (it's been verified in read_inode). */
+ /* Get to the index root value. */
ir = (INDEX_ROOT*)((__u8*)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
@@ -241,5 +241,4 @@
if (!ia) {
Dperror("Failed to allocate buffer for index block");
-
goto put_err_out;
}
@@ -280,6 +279,5 @@
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,
+ "specified size (%u).\n", (long long)vcn,
(unsigned long long)dir_ni->mft_no,
le32_to_cpu(ia->index.allocated_size) + 0x18,
@@ -451,4 +449,477 @@
ntfs_attr_close(ia_na);
goto eo_put_err_out;
+}
+
+typedef union {
+ INDEX_ROOT *ir;
+ INDEX_ALLOCATION *ia;
+} index_union __attribute__ ((__transparent_union__));
+
+typedef enum {
+ INDEX_TYPE_ROOT, /* index root */
+ INDEX_TYPE_ALLOCATION, /* index allocation */
+} INDEX_TYPE;
+
+/*
+ * Internal function:
+ *
+ * ntfs_filldir - ntfs specific filldir method
+ * @dir_ni: ntfs inode of current directory
+ * @pos: current position in directory
+ * @ivcn_bits: log(2) of index vcn size
+ * @index_type: specifies whether @iu is an index root or an index allocation
+ * @iu: index root or index block to which @ie belongs
+ * @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.
+ */
+static inline int ntfs_filldir(ntfs_inode *dir_ni, __s64 *pos, __u8 ivcn_bits,
+ const INDEX_TYPE index_type, index_union iu, INDEX_ENTRY *ie,
+ void *dirent, ntfs_filldir_t filldir)
+{
+ FILE_NAME_ATTR *fn = &ie->key.file_name;
+ unsigned dt_type;
+
+ /* Advance the position even if going to skip the entry. */
+ if (index_type == INDEX_TYPE_ALLOCATION)
+ *pos = (__u8*)ie - (__u8*)iu.ia + (sle64_to_cpu(
+ iu.ia->index_block_vcn) << ivcn_bits) +
+ dir_ni->vol->mft_record_size;
+ else /* if (index_type == INDEX_TYPE_ROOT) */
+ *pos = (__u8*)ie - (__u8*)iu.ir;
+ /* Skip root directory self reference entry. */
+ if (MREF_LE(ie->indexed_file) == FILE_root)
+ return 0;
+ if (ie->key.file_name.file_attributes &
+ FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT)
+ dt_type = NTFS_DT_DIR;
+ else
+ dt_type = NTFS_DT_REG;
+ return filldir(dirent, fn->file_name, fn->file_name_length,
+ fn->file_name_type, *pos,
+ le64_to_cpu(ie->indexed_file), dt_type);
+}
+
+/*
+ * Internal function:
+ *
+ * ntfs_get_parent_mft_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
+ * illegal as Linux/UNIX do not support directory hard links.
+ *
+ * Return the mft reference of the parent directory on success or -1 on error
+ * with errno set to the error code.
+ */
+static MFT_REF ntfs_get_parent_mft_ref(ntfs_inode *ni)
+{
+ MFT_REF mref;
+ ntfs_attr_search_ctx *ctx;
+ FILE_NAME_ATTR *fn;
+ int eo;
+
+ if (!ni) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ctx = ntfs_get_attr_search_ctx(ni, NULL);
+ if (!ctx)
+ return -1;
+ if (ntfs_lookup_attr(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ Dprintf("No file name found in inode 0x%Lx. Corrupt inode.\n",
+ (unsigned long long)ni->mft_no);
+ goto err_out;
+ }
+ if (ctx->attr->non_resident) {
+ Dprintf("File name attribute must be resident. Corrupt inode "
+ "0x%Lx.\n", (unsigned long long)ni->mft_no);
+ goto io_err_out;
+ }
+ fn = (FILE_NAME_ATTR*)((__u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+ if ((__u8*)fn + le32_to_cpu(ctx->attr->value_length) >
+ (__u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
+ Dprintf("Corrupt file name attribute in inode 0x%Lx.\n",
+ (unsigned long long)ni->mft_no);
+ goto io_err_out;
+ }
+ mref = le64_to_cpu(fn->parent_directory);
+ ntfs_put_attr_search_ctx(ctx);
+ return mref;
+io_err_out:
+ errno = EIO;
+err_out:
+ eo = errno;
+ ntfs_put_attr_search_ctx(ctx);
+ errno = eo;
+ return -1;
+}
+
+/**
+ * The little endian Unicode string .. as a static global constant.
+ */
+static const uchar_t dotdot[3] = { const_cpu_to_le16('.'),
+ const_cpu_to_le16('.'),
+ const_cpu_to_le16('\0') };
+
+/**
+ * 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.
+ */
+int ntfs_readdir(ntfs_inode *dir_ni, __s64 *pos,
+ void *dirent, ntfs_filldir_t filldir)
+{
+ __s64 i_size, br, ia_pos, bmp_pos, ia_start;
+ ntfs_volume *vol;
+ ntfs_attr *ia_na, *bmp_na = NULL;
+ ntfs_attr_search_ctx *ctx = NULL;
+ __u8 *index_end, *bmp;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia;
+ int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo;
+ __u32 index_block_size, index_vcn_size;
+ __u8 index_block_size_bits, index_vcn_size_bits;
+
+ if (!dir_ni || !pos || !filldir) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ vol = dir_ni->vol;
+
+ Dprintf("Entering for inode 0x%Lx, *pos 0x%Lx.\n",
+ (unsigned long long)dir_ni->mft_no, (long long)*pos);
+
+ /* Open the index allocation attribute. */
+ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, I30, 4);
+ if (!ia_na) {
+ if (errno != ENOENT) {
+ Dprintf("Failed to open index allocation attribute. "
+ "Directory inode 0x%Lx is corrupt or "
+ "bug: %s\n",
+ (unsigned long long)dir_ni->mft_no,
+ strerror(errno));
+ return -1;
+ }
+ i_size = 0;
+ } else
+ i_size = ia_na->data_size;
+
+ rc = 0;
+
+ /* Are we at end of dir yet? */
+ if (*pos >= i_size + vol->mft_record_size)
+ goto done;
+
+ /* Emulate . and .. for all directories. */
+ if (!*pos) {
+ rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos,
+ MK_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number)),
+ NTFS_DT_DIR);
+ if (rc)
+ goto done;
+ ++*pos;
+ }
+ if (*pos == 1) {
+ MFT_REF parent_mref;
+
+ parent_mref = ntfs_get_parent_mft_ref(dir_ni);
+ if (parent_mref == -1) {
+ Dprintf("Parent directory not found: %s\n", errno);
+ goto dir_err_out;
+ }
+
+ rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos,
+ parent_mref, NTFS_DT_DIR);
+ if (rc)
+ goto done;
+ ++*pos;
+ }
+
+ ctx = ntfs_get_attr_search_ctx(dir_ni, NULL);
+ if (!ctx)
+ goto err_out;
+
+ /* Get the offset into the index root attribute. */
+ ir_pos = (int)*pos;
+ /* 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.\n", (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* Get to the index root value. */
+ ir = (INDEX_ROOT*)((__u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->value_offset));
+
+ /* Determine the size of a vcn in the directory index. */
+ 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 dir_err_out;
+ }
+ index_block_size_bits = ffs(index_block_size) - 1;
+ 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;
+ }
+
+ /* Are we jumping straight into the index allocation attribute? */
+ if (*pos >= vol->mft_record_size) {
+ ntfs_put_attr_search_ctx(ctx);
+ ctx = NULL;
+ goto skip_index_root;
+ }
+
+ 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 or until filldir tells us it has had enough
+ * or signals an error (both covered by the rc test).
+ */
+ for (;; ie = (INDEX_ENTRY*)((__u8*)ie + le16_to_cpu(ie->length))) {
+ Dprintf("In index root, offset 0x%x.\n", (__u8*)ie - (__u8*)ir);
+ /* 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 dir_err_out;
+ /* The last entry cannot contain a name. */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /* Skip index root entry if continuing previous readdir. */
+ if (ir_pos > (__u8*)ie - (__u8*)ir)
+ continue;
+ /*
+ * Submit the directory entry to ntfs_filldir(), which will
+ * invoke the filldir() callback as appropriate.
+ */
+ rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits,
+ INDEX_TYPE_ROOT, ir, ie, dirent, filldir);
+ if (rc) {
+ ntfs_put_attr_search_ctx(ctx);
+ ctx = NULL;
+ goto done;
+ }
+ }
+ ntfs_put_attr_search_ctx(ctx);
+ ctx = NULL;
+
+ /* If there is no index allocation attribute we are finished. */
+ if (!ia_na)
+ goto EOD;
+
+ /* Advance *pos to the beginning of the index allocation. */
+ *pos = vol->mft_record_size;
+
+skip_index_root:
+
+ if (!ia_na)
+ goto done;
+
+ /* 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 err_out;
+ }
+
+ bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, I30, 4);
+ if (!bmp_na) {
+ Dperror("Failed to open index bitmap attribute");
+ goto dir_err_out;
+ }
+
+ /* Get the offset into the index allocation attribute. */
+ ia_pos = *pos - vol->mft_record_size;
+
+ bmp_pos = ia_pos >> index_block_size_bits;
+ if (bmp_pos >> 3 >= bmp_na->data_size) {
+ Dputs("Current index position exceeds index bitmap size.");
+ goto dir_err_out;
+ }
+
+ bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096);
+ bmp = (__u8*)malloc(bmp_buf_size);
+ if (!bmp) {
+ Dperror("Failed to allocate bitmap buffer");
+ goto err_out;
+ }
+
+ br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
+ if (br != bmp_buf_size) {
+ if (br != -1)
+ errno = EIO;
+ Dperror("Failed to read from inde bitmap attribute");
+ goto err_out;
+ }
+
+ bmp_buf_pos = 0;
+ /* If the index block is not in use find the next one that is. */
+ while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) {
+find_next_index_buffer:
+ bmp_pos++;
+ bmp_buf_pos++;
+ /* If we have reached the end of the bitmap, we are done. */
+ if (bmp_pos >> 3 >= bmp_na->data_size)
+ goto EOD;
+ ia_pos = bmp_pos << index_block_size_bits;
+ if (bmp_buf_pos >> 3 < bmp_buf_size)
+ continue;
+ /* Read next chunk from the index bitmap. */
+ if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size)
+ bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3);
+ br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
+ if (br != bmp_buf_size) {
+ if (br != -1)
+ errno = EIO;
+ Dperror("Failed to read from inde bitmap attribute");
+ goto err_out;
+ }
+ }
+
+ Dprintf("Handling index block 0x%Lx.", (long long)bmp_pos);
+
+ /* Read the index block starting at bmp_pos. */
+ br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1,
+ index_block_size, ia);
+ if (br != 1) {
+ if (br != -1)
+ errno = EIO;
+ Dperror("Failed to read index block");
+ goto err_out;
+ }
+
+ ia_start = ia_pos & ~(__s64)(index_block_size - 1);
+ if (sle64_to_cpu(ia->index_block_vcn) != ia_start >>
+ index_vcn_size_bits) {
+ Dprintf("Actual VCN (0x%Lx) of index buffer is different from "
+ "expected VCN (0x%Lx) in inode 0x%Lx.\n",
+ (long long)sle64_to_cpu(ia->index_block_vcn),
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no);
+ goto dir_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).\n",
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no,
+ le32_to_cpu(ia->index.allocated_size) + 0x18,
+ index_block_size);
+ goto dir_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)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((__u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * 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).
+ */
+ for (;; ie = (INDEX_ENTRY*)((__u8*)ie + le16_to_cpu(ie->length))) {
+ Dprintf("In index allocation, offset 0x%Lx.\n",
+ (long long)ia_start + ((__u8*)ie - (__u8*)ia));
+ /* Bounds checks. */
+ 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);
+ goto dir_err_out;
+ }
+ /* The last entry cannot contain a name. */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /* Skip index entry if continuing previous readdir. */
+ if (ia_pos - ia_start > (__u8*)ie - (__u8*)ia)
+ continue;
+ /*
+ * Submit the directory entry to ntfs_filldir(), which will
+ * invoke the filldir() callback as appropriate.
+ */
+ rc = ntfs_filldir(dir_ni, pos, index_vcn_size_bits,
+ INDEX_TYPE_ALLOCATION, ia, ie, dirent, filldir);
+ if (rc)
+ goto done;
+ }
+ goto find_next_index_buffer;
+
+
+
+EOD:
+ /* We are finished, set *pos to EOD. */
+ *pos = i_size + vol->mft_record_size;
+done:
+ if (bmp_na)
+ ntfs_attr_close(bmp_na);
+ ntfs_attr_close(ia_na);
+#ifdef DEBUG
+ if (!rc)
+ Dprintf("EOD, *pos 0x%Lx, returning 0.\n", (long long)*pos);
+ else
+ Dprintf("filldir returned %i, *pos 0x%Lx, returning 0.\n",
+ rc, (long long)*pos);
+#endif
+ return 0;
+dir_err_out:
+ errno = EIO;
+err_out:
+ eo = errno;
+ Dputs(__FUNCTION__ "() failed.");
+ if (ctx)
+ ntfs_put_attr_search_ctx(ctx);
+ if (bmp_na)
+ ntfs_attr_close(bmp_na);
+ ntfs_attr_close(ia_na);
+ errno = eo;
+ return -1;
}
Index: mft.c
===================================================================
RCS file: /cvsroot/linux-ntfs/linux-ntfs/libntfs/mft.c,v
retrieving revision 1.27
retrieving revision 1.28
diff -U2 -r1.27 -r1.28
--- mft.c 5 Jun 2002 20:32:53 -0000 1.27
+++ mft.c 8 Jun 2002 14:12:01 -0000 1.28
@@ -224,5 +224,5 @@
return 0;
file_corrupt:
- Dputs("read_file_record(): file is corrupt.");
+ Dputs("ntfs_read_file_record(): file is corrupt.");
err = EIO;
read_failed:
|