dir.c
来自「一个类似windows」· C语言 代码 · 共 1,421 行 · 第 1/4 页
C
1,421 行
u8 *name = NULL;
int rc, err, ir_pos, cur_bmp_pos;
struct address_space *ia_mapping, *bmp_mapping;
struct page *bmp_page = NULL, *ia_page = NULL;
u8 *kaddr, *bmp, *index_end;
attr_search_context *ctx;
fpos = filp->f_pos;
ntfs_debug("Entering for inode 0x%lx, fpos 0x%Lx.",
vdir->i_ino, fpos);
rc = err = 0;
/* Are we at end of dir yet? */
if (fpos >= vdir->i_size + vol->mft_record_size)
goto done;
/* Emulate . and .. for all directories. */
if (!fpos) {
ntfs_debug("Calling filldir for . with len 1, fpos 0x0, "
"inode 0x%lx, DT_DIR.", vdir->i_ino);
rc = filldir(dirent, ".", 1, fpos, vdir->i_ino, DT_DIR);
if (rc)
goto done;
fpos++;
}
if (fpos == 1) {
ntfs_debug("Calling filldir for .. with len 2, fpos 0x1, "
"inode 0x%lx, DT_DIR.",
parent_ino(filp->f_dentry));
rc = filldir(dirent, "..", 2, fpos,
parent_ino(filp->f_dentry), DT_DIR);
if (rc)
goto done;
fpos++;
}
m = NULL;
ctx = NULL;
/*
* Allocate a buffer to store the current name being processed
* converted to format determined by current NLS.
*/
name = (u8*)kmalloc(NTFS_MAX_NAME_LEN * NLS_MAX_CHARSET_SIZE + 1,
GFP_NOFS);
if (unlikely(!name)) {
err = -ENOMEM;
goto err_out;
}
/* Are we jumping straight into the index allocation attribute? */
if (fpos >= vol->mft_record_size)
goto skip_index_root;
/* Get hold of the mft record for the directory. */
m = map_mft_record(ndir);
if (unlikely(IS_ERR(m))) {
err = PTR_ERR(m);
m = NULL;
goto err_out;
}
ctx = get_attr_search_ctx(ndir, m);
if (unlikely(!ctx)) {
err = -ENOMEM;
goto err_out;
}
/* Get the offset into the index root attribute. */
ir_pos = (s64)fpos;
/* Find the index root attribute in the mft record. */
if (unlikely(!lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0,
NULL, 0, ctx))) {
ntfs_error(sb, "Index root attribute missing in directory "
"inode 0x%lx.", vdir->i_ino);
goto 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->data.resident.value_offset));
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))) {
ntfs_debug("In index root, offset 0x%x.", (u8*)ie - (u8*)ir);
/* Bounds checks. */
if (unlikely((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
index_end))
goto 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 name to the filldir callback. */
rc = ntfs_filldir(vol, &fpos, ndir, INDEX_TYPE_ROOT, ir, ie,
name, dirent, filldir);
if (rc) {
put_attr_search_ctx(ctx);
unmap_mft_record(ndir);
goto abort;
}
}
/*
* We are done with the index root and the mft record for that matter.
* We need to release it, otherwise we deadlock on ntfs_attr_iget()
* and/or ntfs_read_page().
*/
put_attr_search_ctx(ctx);
unmap_mft_record(ndir);
m = NULL;
ctx = NULL;
/* If there is no index allocation attribute we are finished. */
if (!NInoIndexAllocPresent(ndir))
goto EOD;
/* Advance fpos to the beginning of the index allocation. */
fpos = vol->mft_record_size;
skip_index_root:
kaddr = NULL;
prev_ia_pos = -1LL;
/* Get the offset into the index allocation attribute. */
ia_pos = (s64)fpos - vol->mft_record_size;
ia_mapping = vdir->i_mapping;
bmp_vi = ndir->itype.index.bmp_ino;
if (unlikely(!bmp_vi)) {
ntfs_debug("Inode %lu, regetting index bitmap.", vdir->i_ino);
bmp_vi = ntfs_attr_iget(vdir, AT_BITMAP, I30, 4);
if (unlikely(IS_ERR(bmp_vi))) {
ntfs_error(sb, "Failed to get bitmap attribute.");
err = PTR_ERR(bmp_vi);
goto err_out;
}
ndir->itype.index.bmp_ino = bmp_vi;
}
bmp_mapping = bmp_vi->i_mapping;
/* Get the starting bitmap bit position and sanity check it. */
bmp_pos = ia_pos >> ndir->itype.index.block_size_bits;
if (unlikely(bmp_pos >> 3 >= bmp_vi->i_size)) {
ntfs_error(sb, "Current index allocation position exceeds "
"index bitmap size.");
goto err_out;
}
/* Get the starting bit position in the current bitmap page. */
cur_bmp_pos = bmp_pos & ((PAGE_CACHE_SIZE * 8) - 1);
bmp_pos &= ~(u64)((PAGE_CACHE_SIZE * 8) - 1);
get_next_bmp_page:
ntfs_debug("Reading bitmap with page index 0x%Lx, bit ofs 0x%Lx",
(long long)bmp_pos >> (3 + PAGE_CACHE_SHIFT),
(long long)bmp_pos & ((PAGE_CACHE_SIZE * 8) - 1));
bmp_page = ntfs_map_page(bmp_mapping,
bmp_pos >> (3 + PAGE_CACHE_SHIFT));
if (unlikely(IS_ERR(bmp_page))) {
ntfs_error(sb, "Reading index bitmap failed.");
err = PTR_ERR(bmp_page);
bmp_page = NULL;
goto err_out;
}
bmp = (u8*)page_address(bmp_page);
/* Find next index block in use. */
while (!(bmp[cur_bmp_pos >> 3] & (1 << (cur_bmp_pos & 7)))) {
find_next_index_buffer:
cur_bmp_pos++;
/*
* If we have reached the end of the bitmap page, get the next
* page, and put away the old one.
*/
if (unlikely((cur_bmp_pos >> 3) >= PAGE_CACHE_SIZE)) {
ntfs_unmap_page(bmp_page);
bmp_pos += PAGE_CACHE_SIZE * 8;
cur_bmp_pos = 0;
goto get_next_bmp_page;
}
/* If we have reached the end of the bitmap, we are done. */
if (unlikely(((bmp_pos + cur_bmp_pos) >> 3) >= vdir->i_size))
goto unm_EOD;
ia_pos = (bmp_pos + cur_bmp_pos) <<
ndir->itype.index.block_size_bits;
}
ntfs_debug("Handling index buffer 0x%Lx.",
(long long)bmp_pos + cur_bmp_pos);
/* If the current index buffer is in the same page we reuse the page. */
if ((prev_ia_pos & PAGE_CACHE_MASK) != (ia_pos & PAGE_CACHE_MASK)) {
prev_ia_pos = ia_pos;
if (likely(ia_page != NULL))
ntfs_unmap_page(ia_page);
/*
* Map the page cache page containing the current ia_pos,
* reading it from disk if necessary.
*/
ia_page = ntfs_map_page(ia_mapping, ia_pos >> PAGE_CACHE_SHIFT);
if (unlikely(IS_ERR(ia_page))) {
ntfs_error(sb, "Reading index allocation data failed.");
err = PTR_ERR(ia_page);
ia_page = NULL;
goto err_out;
}
kaddr = (u8*)page_address(ia_page);
}
/* Get the current index buffer. */
ia = (INDEX_ALLOCATION*)(kaddr + (ia_pos & ~PAGE_CACHE_MASK &
~(s64)(ndir->itype.index.block_size - 1)));
/* Bounds checks. */
if (unlikely((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE)) {
ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
"inode 0x%lx or driver bug.", vdir->i_ino);
goto err_out;
}
if (unlikely(sle64_to_cpu(ia->index_block_vcn) != (ia_pos &
~(s64)(ndir->itype.index.block_size - 1)) >>
ndir->itype.index.vcn_size_bits)) {
ntfs_error(sb, "Actual VCN (0x%Lx) of index buffer is "
"different from expected VCN (0x%Lx). "
"Directory inode 0x%lx is corrupt or driver "
"bug. ",
(long long)sle64_to_cpu(ia->index_block_vcn),
(long long)ia_pos >>
ndir->itype.index.vcn_size_bits, vdir->i_ino);
goto err_out;
}
if (unlikely(le32_to_cpu(ia->index.allocated_size) + 0x18 !=
ndir->itype.index.block_size)) {
ntfs_error(sb, "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.",
(long long)ia_pos >>
ndir->itype.index.vcn_size_bits, vdir->i_ino,
le32_to_cpu(ia->index.allocated_size) + 0x18,
ndir->itype.index.block_size);
goto err_out;
}
index_end = (u8*)ia + ndir->itype.index.block_size;
if (unlikely(index_end > kaddr + PAGE_CACHE_SIZE)) {
ntfs_error(sb, "Index buffer (VCN 0x%Lx) of directory inode "
"0x%lx crosses page boundary. Impossible! "
"Cannot access! This is probably a bug in the "
"driver.", (long long)ia_pos >>
ndir->itype.index.vcn_size_bits, vdir->i_ino);
goto err_out;
}
ia_start = ia_pos & ~(s64)(ndir->itype.index.block_size - 1);
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
if (unlikely(index_end > (u8*)ia + ndir->itype.index.block_size)) {
ntfs_error(sb, "Size of index buffer (VCN 0x%Lx) of directory "
"inode 0x%lx exceeds maximum size.",
(long long)ia_pos >>
ndir->itype.index.vcn_size_bits, vdir->i_ino);
goto err_out;
}
/* The first index entry in this index buffer. */
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 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))) {
ntfs_debug("In index allocation, offset 0x%Lx.",
(long long)ia_start + ((u8*)ie - (u8*)ia));
/* Bounds checks. */
if (unlikely((u8*)ie < (u8*)ia || (u8*)ie +
sizeof(INDEX_ENTRY_HEADER) > index_end ||
(u8*)ie + le16_to_cpu(ie->key_length) >
index_end))
goto err_out;
/* The last entry cannot contain a name. */
if (ie->flags & INDEX_ENTRY_END)
break;
/* Skip index block entry if continuing previous readdir. */
if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
continue;
/* Submit the name to the filldir callback. */
rc = ntfs_filldir(vol, &fpos, ndir, INDEX_TYPE_ALLOCATION, ia,
ie, name, dirent, filldir);
if (rc) {
ntfs_unmap_page(ia_page);
ntfs_unmap_page(bmp_page);
goto abort;
}
}
goto find_next_index_buffer;
unm_EOD:
if (ia_page)
ntfs_unmap_page(ia_page);
ntfs_unmap_page(bmp_page);
EOD:
/* We are finished, set fpos to EOD. */
fpos = vdir->i_size + vol->mft_record_size;
abort:
kfree(name);
done:
#ifdef DEBUG
if (!rc)
ntfs_debug("EOD, fpos 0x%Lx, returning 0.", fpos);
else
ntfs_debug("filldir returned %i, fpos 0x%Lx, returning 0.",
rc, fpos);
#endif
filp->f_pos = fpos;
return 0;
err_out:
if (bmp_page)
ntfs_unmap_page(bmp_page);
if (ia_page)
ntfs_unmap_page(ia_page);
if (name)
kfree(name);
if (ctx)
put_attr_search_ctx(ctx);
if (m)
unmap_mft_record(ndir);
if (!err)
err = -EIO;
ntfs_debug("Failed. Returning error code %i.", -err);
filp->f_pos = fpos;
return err;
}
/**
* ntfs_dir_open - called when an inode is about to be opened
* @vi: inode to be opened
* @filp: file structure describing the inode
*
* Limit directory size to the page cache limit on architectures where unsigned
* long is 32-bits. This is the most we can do for now without overflowing the
* page cache page index. Doing it this way means we don't run into problems
* because of existing too large directories. It would be better to allow the
* user to read the accessible part of the directory but I doubt very much
* anyone is going to hit this check on a 32-bit architecture, so there is no
* point in adding the extra complexity required to support this.
*
* On 64-bit architectures, the check is hopefully optimized away by the
* compiler.
*/
static int ntfs_dir_open(struct inode *vi, struct file *filp)
{
if (sizeof(unsigned long) < 8) {
if (vi->i_size > MAX_LFS_FILESIZE)
return -EFBIG;
}
return 0;
}
struct file_operations ntfs_dir_ops = {
.llseek = generic_file_llseek, /* Seek inside directory. */
.read = generic_read_dir, /* Return -EISDIR. */
.readdir = ntfs_readdir, /* Read directory contents. */
.open = ntfs_dir_open, /* Open directory. */
};
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?