dir.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:30k
- /*
- * dir.c
- *
- * Copyright (C) 1995-1997, 1999 Martin von L鰓is
- * Copyright (C) 1999 Steve Dodd
- * Copyright (C) 1999 Joseph Malicki
- * Copyright (C) 2001 Anton Altaparmakov (AIA)
- */
- #include "ntfstypes.h"
- #include "struct.h"
- #include "dir.h"
- #include "macros.h"
- #include <linux/errno.h>
- #include "super.h"
- #include "inode.h"
- #include "attr.h"
- #include "support.h"
- #include "util.h"
- #include <linux/smp_lock.h>
- #include <linux/bitops.h>
- static char I30[] = "$I30";
- /* An index record should start with INDX, and the last word in each block
- * should contain the check value. If it passes, the original values need to
- * be restored. */
- int ntfs_check_index_record(ntfs_inode *ino, char *record)
- {
- return ntfs_fixup_record(record, "INDX", ino->u.index.recordsize);
- }
- static inline int ntfs_is_top(ntfs_u64 stack)
- {
- return stack == 14;
- }
- static int ntfs_pop(ntfs_u64 *stack)
- {
- static int width[16] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
- int res = -1;
-
- switch (width[*stack & 15]) {
- case 1:
- res = (int)((*stack & 15) >> 1);
- *stack >>= 4;
- break;
- case 2:
- res = (int)(((*stack & 63) >> 2) + 7);
- *stack >>= 6;
- break;
- case 3:
- res = (int)(((*stack & 255) >> 3) + 23);
- *stack >>= 8;
- break;
- case 4:
- res = (int)(((*stack & 1023) >> 4) + 55);
- *stack >>= 10;
- break;
- default:
- ntfs_error("Unknown encodingn");
- }
- return res;
- }
- static inline unsigned int ntfs_top(void)
- {
- return 14;
- }
- static ntfs_u64 ntfs_push(ntfs_u64 stack, int i)
- {
- if (i < 7)
- return (stack << 4) | (i << 1);
- if (i < 23)
- return (stack << 6) | ((i - 7) << 2) | 1;
- if (i < 55)
- return (stack << 8) | ((i - 23) << 3) | 3;
- if (i < 120)
- return (stack << 10) | ((i - 55) << 4) | 7;
- ntfs_error("Too many entriesn");
- return ~((ntfs_u64)0);
- }
- #if 0
- static void ntfs_display_stack(ntfs_u64 stack)
- {
- while(!ntfs_is_top(stack))
- {
- printf("%d ", ntfs_pop(&stack));
- }
- printf("n");
- }
- #endif
- /* True if the entry points to another block of entries. */
- static inline int ntfs_entry_has_subnodes(char *entry)
- {
- return (NTFS_GETU16(entry + 0xc) & 1);
- }
- /* True if it is not the 'end of dir' entry. */
- static inline int ntfs_entry_is_used(char *entry)
- {
- return !(NTFS_GETU16(entry + 0xc) & 2);
- }
- /*
- * Removed RACE for allocating index blocks. But stil not too happy.
- * There might be more races afterwards. (AIA)
- */
- static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
- {
- ntfs_attribute *allocation, *bitmap = 0;
- int error, size, i, bit;
- ntfs_u8 *bmap;
- ntfs_io io;
- ntfs_volume *vol = walk->dir->vol;
- /* Check for allocation attribute. */
- allocation = ntfs_find_attr(walk->dir, vol->at_index_allocation, I30);
- if (!allocation) {
- ntfs_u8 bmp[8];
- /* Create index allocation attribute. */
- error = ntfs_create_attr(walk->dir, vol->at_index_allocation,
- I30, 0, 0, &allocation);
- if (error)
- goto err_ret;
- ntfs_bzero(bmp, sizeof(bmp));
- error = ntfs_create_attr(walk->dir, vol->at_bitmap, I30, bmp,
- sizeof(bmp), &bitmap);
- if (error)
- goto err_ret;
- } else
- bitmap = ntfs_find_attr(walk->dir, vol->at_bitmap, I30);
- if (!bitmap) {
- ntfs_error("Directory w/o bitmapn");
- error = -EINVAL;
- goto err_ret;
- }
- size = bitmap->size;
- bmap = ntfs_malloc(size);
- if (!bmap) {
- error = -ENOMEM;
- goto err_ret;
- }
- io.fn_put = ntfs_put;
- io.fn_get = ntfs_get;
- try_again:
- io.param = bmap;
- io.size = size;
- error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, 0, &io);
- if (error || (io.size != size && (error = -EIO, 1)))
- goto err_fb_out;
- /* Allocate a bit. */
- for (bit = i = 0; i < size; i++) {
- if (bmap[i] == 0xFF)
- continue;
- bit = ffz(bmap[i]);
- if (bit < 8)
- break;
- }
- if (i >= size) {
- /* FIXME: Extend bitmap. */
- error = -EOPNOTSUPP;
- goto err_fb_out;
- }
- /* Get the byte containing our bit again, now taking the BKL. */
- io.param = bmap;
- io.size = 1;
- lock_kernel();
- error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, i, &io);
- if (error || (io.size != 1 && (error = -EIO, 1)))
- goto err_unl_out;
- if (ntfs_test_and_set_bit(bmap, bit)) {
- unlock_kernel();
- /* Give other process(es) a chance to finish. */
- schedule();
- goto try_again;
- }
- walk->newblock = (i * 8 + bit) * walk->dir->u.index.clusters_per_record;
- io.param = bmap;
- error = ntfs_write_attr(walk->dir, vol->at_bitmap, I30, i, &io);
- if (error || (io.size != size && (error = -EIO, 1)))
- goto err_unl_out;
- /* Change inode on disk, required when bitmap is resident. */
- error = ntfs_update_inode(walk->dir);
- if (error)
- goto err_unl_out;
- unlock_kernel();
- ntfs_free(bmap);
- /* Check whether record is out of allocated range. */
- size = allocation->size;
- if (walk->newblock * vol->cluster_size >= size) {
- /* Build index record. */
- int hsize;
- int s1 = walk->dir->u.index.recordsize;
- int nr_fix = (s1 >> vol->sector_size) + 1;
- char *record = ntfs_malloc(s1);
- if (!record) {
- error = -ENOMEM;
- goto err_ret;
- }
- ntfs_bzero(record, s1);
- /* Magic */
- ntfs_memcpy(record, "INDX", 4);
- /* Offset to fixups */
- NTFS_PUTU16(record + 4, 0x28);
- /* Number of fixups. */
- NTFS_PUTU16(record + 6, nr_fix);
- /* Log file sequence number - We don't do journalling so we
- * just set it to zero which should be the Right Thing. (AIA) */
- NTFS_PUTU64(record + 8, 0);
- /* VCN of buffer */
- NTFS_PUTU64(record + 0x10, walk->newblock);
- /* Header size. */
- hsize = 0x10 + 2 * nr_fix;
- hsize = (hsize + 7) & ~7; /* Align. */
- NTFS_PUTU16(record + 0x18, hsize);
- /* Total size of record. */
- NTFS_PUTU32(record + 0x20, s1 - 0x18);
- /* Writing the data will extend the attribute. */
- io.param = record;
- io.size = s1;
- io.do_read = 0;
- error = ntfs_readwrite_attr(walk->dir, allocation, size, &io);
- ntfs_free(record);
- if (error || (io.size != s1 && (error = -EIO, 1)))
- goto err_ret;
- error = ntfs_update_inode(walk->dir);
- if (error)
- goto err_ret;
- }
- return 0;
- err_unl_out:
- unlock_kernel();
- err_fb_out:
- ntfs_free(bmap);
- err_ret:
- return error;
- }
- /* Write an index block (root or allocation) back to storage.
- * Used is the total number of bytes in buf, including all headers. */
- static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
- int used)
- {
- ntfs_io io;
- int error;
- ntfs_attribute *a;
- ntfs_volume *vol = walk->dir->vol;
-
- io.fn_put = 0;
- io.fn_get = ntfs_get;
- io.param = buf;
- if (block == -1) { /* Index root. */
- NTFS_PUTU16(buf + 0x14, used - 0x10);
- /* 0x18 is a copy thereof. */
- NTFS_PUTU16(buf + 0x18, used - 0x10);
- io.size = used;
- error = ntfs_write_attr(walk->dir, vol->at_index_root, I30, 0,
- &io);
- if (error || (io.size != used && (error = -EIO, 1)))
- return error;
- /* Shrink if necessary. */
- a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
- ntfs_resize_attr(walk->dir, a, used);
- } else {
- NTFS_PUTU16(buf + 0x1C, used - 0x18);
- io.size = walk->dir->u.index.recordsize;
- error = ntfs_insert_fixups(buf, io.size);
- if (error) {
- printk(KERN_ALERT "NTFS: ntfs_index_writeback() caught "
- "corrupt index record ntfs record "
- "header. Refusing to write corrupt "
- "data to disk. Unmount and run chkdsk "
- "immediately!n");
- return -EIO;
- }
- error = ntfs_write_attr(walk->dir, vol->at_index_allocation,
- I30, (__s64)block << vol->cluster_size_bits,
- &io);
- if (error || (io.size != walk->dir->u.index.recordsize &&
- (error = -EIO, 1)))
- return error;
- }
- return 0;
- }
- static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
- int usize)
- {
- char *entry, *prev;
- ntfs_u8 *newbuf = 0, *middle = 0;
- int error, othersize, mlen;
- ntfs_io io;
- ntfs_volume *vol = walk->dir->vol;
- int oldblock;
- error = ntfs_allocate_index_block(walk);
- if (error)
- return error;
- /* This should not happen. */
- if (walk->block == -1) {
- ntfs_error("Trying to split root");
- return -EOPNOTSUPP;
- }
- entry = start + NTFS_GETU16(start + 0x18) + 0x18;
- for (prev = entry; entry - start < usize / 2;
- entry += NTFS_GETU16(entry + 8))
- prev = entry;
- newbuf = ntfs_malloc(vol->index_record_size);
- if (!newbuf)
- return -ENOMEM;
- io.fn_put = ntfs_put;
- io.fn_get = ntfs_get;
- io.param = newbuf;
- io.size = vol->index_record_size;
- /* Read in old header. FIXME: Reading everything is overkill. */
- error = ntfs_read_attr(walk->dir, vol->at_index_allocation, I30,
- (__s64)walk->newblock << vol->cluster_size_bits, &io);
- if (error)
- goto out;
- if (io.size != vol->index_record_size) {
- error = -EIO;
- goto out;
- }
- /* FIXME: Adjust header. */
- /* Copy everything from entry to new block. */
- othersize = usize - (entry - start);
- ntfs_memcpy(newbuf + NTFS_GETU16(newbuf + 0x18) + 0x18, entry,
- othersize);
- /* Copy flags. */
- NTFS_PUTU32(newbuf + 0x24, NTFS_GETU32(start + 0x24));
- error = ntfs_index_writeback(walk, newbuf, walk->newblock,
- othersize + NTFS_GETU16(newbuf + 0x18) + 0x18);
- if (error)
- goto out;
- /* Move prev to walk. */
- mlen = NTFS_GETU16(prev + 0x8);
- /* Remember old child node. */
- if (ntfs_entry_has_subnodes(prev))
- oldblock = NTFS_GETU32(prev + mlen - 8);
- else
- oldblock = -1;
- /* Allow for pointer to subnode. */
- middle = ntfs_malloc(ntfs_entry_has_subnodes(prev) ? mlen : mlen + 8);
- if (!middle){
- error = -ENOMEM;
- goto out;
- }
- ntfs_memcpy(middle, prev, mlen);
- /* Set has_subnodes flag. */
- NTFS_PUTU8(middle + 0xC, NTFS_GETU8(middle + 0xC) | 1);
- /* Middle entry points to block, parent entry will point to newblock. */
- NTFS_PUTU64(middle + mlen - 8, walk->block);
- if (walk->new_entry)
- ntfs_error("Entry not reset");
- walk->new_entry = middle;
- walk->u.flags |= ITERATE_SPLIT_DONE;
- /* Terminate old block. */
- othersize = usize - (prev-start);
- NTFS_PUTU64(prev, 0);
- if (oldblock == -1) {
- NTFS_PUTU32(prev + 8, 0x10);
- NTFS_PUTU32(prev + 0xC, 2);
- othersize += 0x10;
- } else {
- NTFS_PUTU32(prev + 8, 0x18);
- NTFS_PUTU32(prev + 0xC, 3);
- NTFS_PUTU64(prev + 0x10, oldblock);
- othersize += 0x18;
- }
- /* Write back original block. */
- error = ntfs_index_writeback(walk, start, walk->block, othersize);
- out:
- if (newbuf)
- ntfs_free(newbuf);
- if (middle)
- ntfs_free(middle);
- return error;
- }
- static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
- {
- int blocksize, usedsize, error, offset;
- int do_split = 0;
- offset = entry - start;
- if (walk->block == -1) { /* index root */
- blocksize = walk->dir->vol->mft_record_size;
- usedsize = NTFS_GETU16(start + 0x14) + 0x10;
- } else {
- blocksize = walk->dir->u.index.recordsize;
- usedsize = NTFS_GETU16(start + 0x1C) + 0x18;
- }
- if (usedsize + walk->new_entry_size > blocksize) {
- char* s1 = ntfs_malloc(blocksize + walk->new_entry_size);
- if (!s1)
- return -ENOMEM;
- ntfs_memcpy(s1, start, usedsize);
- do_split = 1;
- /* Adjust entry to s1. */
- entry = s1 + (entry - start);
- start = s1;
- }
- ntfs_memmove(entry + walk->new_entry_size, entry, usedsize - offset);
- ntfs_memcpy(entry, walk->new_entry, walk->new_entry_size);
- usedsize += walk->new_entry_size;
- ntfs_free(walk->new_entry);
- walk->new_entry = 0;
- if (do_split) {
- error = ntfs_split_record(walk, start, blocksize, usedsize);
- ntfs_free(start);
- } else {
- error = ntfs_index_writeback(walk, start, walk->block,usedsize);
- if (error)
- return error;
- }
- return 0;
- }
- /* Try to split INDEX_ROOT attributes. Return -E2BIG if nothing changed. */
- int ntfs_split_indexroot(ntfs_inode *ino)
- {
- ntfs_attribute *ra;
- ntfs_u8 *root = 0, *index = 0;
- ntfs_io io;
- int error, off, i, bsize, isize;
- ntfs_iterate_s walk;
- ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
- if (!ra)
- return -ENOTDIR;
- bsize = ino->vol->mft_record_size;
- root = ntfs_malloc(bsize);
- if (!root)
- return -E2BIG;
- io.fn_put = ntfs_put;
- io.param = root;
- io.size = bsize;
- error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
- if (error)
- goto out;
- off = 0x20;
- /* Count number of entries. */
- for (i = 0; ntfs_entry_is_used(root + off); i++)
- off += NTFS_GETU16(root + off + 8);
- if (i <= 2) {
- /* We don't split small index roots. */
- error = -E2BIG;
- goto out;
- }
- index = ntfs_malloc(ino->vol->index_record_size);
- if (!index) {
- error = -ENOMEM;
- goto out;
- }
- walk.dir = ino;
- walk.block = -1;
- walk.result = walk.new_entry = 0;
- walk.name = 0;
- error = ntfs_allocate_index_block(&walk);
- if (error)
- goto out;
- /* Write old root to new index block. */
- io.param = index;
- io.size = ino->vol->index_record_size;
- error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
- (__s64)walk.newblock << ino->vol->cluster_size_bits, &io);
- if (error)
- goto out;
- isize = NTFS_GETU16(root + 0x18) - 0x10;
- ntfs_memcpy(index + NTFS_GETU16(index + 0x18) + 0x18, root+0x20, isize);
- /* Copy flags. */
- NTFS_PUTU32(index + 0x24, NTFS_GETU32(root + 0x1C));
- error = ntfs_index_writeback(&walk, index, walk.newblock,
- isize + NTFS_GETU16(index + 0x18) + 0x18);
- if (error)
- goto out;
- /* Mark root as split. */
- NTFS_PUTU32(root + 0x1C, 1);
- /* Truncate index root. */
- NTFS_PUTU64(root + 0x20, 0);
- NTFS_PUTU32(root + 0x28, 0x18);
- NTFS_PUTU32(root + 0x2C, 3);
- NTFS_PUTU64(root + 0x30, walk.newblock);
- error = ntfs_index_writeback(&walk, root, -1, 0x38);
- out:
- ntfs_free(root);
- ntfs_free(index);
- return error;
- }
- /* The entry has been found. Copy the result in the caller's buffer */
- static int ntfs_copyresult(char *dest, char *source)
- {
- int length = NTFS_GETU16(source + 8);
- ntfs_memcpy(dest, source, length);
- return 1;
- }
- /* Use $UpCase some day. */
- static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
- {
- /* We should read any pending rest of $UpCase here. */
- if (x >= vol->upcase_length)
- return x;
- return vol->upcase[x];
- }
- /* Everything passed in walk and entry. */
- static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
- {
- int lu = *(entry + 0x50);
- int i;
- ntfs_u16* name = (ntfs_u16*)(entry + 0x52);
- ntfs_volume *vol = walk->dir->vol;
- for (i = 0; i < lu && i < walk->namelen; i++)
- if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) !=
- ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
- break;
- if (i == lu && i == walk->namelen)
- return 0;
- if (i == lu)
- return 1;
- if (i == walk->namelen)
- return -1;
- if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) <
- ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
- return 1;
- return -1;
- }
- /* Necessary forward declaration. */
- static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
- /* Parse a block of entries. Load the block, fix it up, and iterate over the
- * entries. The block is given as virtual cluster number. */
- static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
- {
- int length = walk->dir->u.index.recordsize;
- char *record = (char*)ntfs_malloc(length);
- char *offset;
- int retval,error;
- int oldblock;
- ntfs_io io;
- if (!record)
- return -ENOMEM;
- io.fn_put = ntfs_put;
- io.param = record;
- io.size = length;
- /* Read the block from the index allocation attribute. */
- error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_allocation,
- I30, (__s64)block << walk->dir->vol->cluster_size_bits, &io);
- if (error || io.size != length) {
- ntfs_error("read failedn");
- ntfs_free(record);
- return 0;
- }
- if (!ntfs_check_index_record(walk->dir, record)) {
- ntfs_error("%x is not an index recordn", block);
- ntfs_free(record);
- return 0;
- }
- offset = record + NTFS_GETU16(record + 0x18) + 0x18;
- oldblock = walk->block;
- walk->block = block;
- retval = ntfs_getdir_iterate(walk, record, offset);
- walk->block = oldblock;
- ntfs_free(record);
- return retval;
- }
- /* Go down to the next block of entries. These collate before the current
- * entry. */
- static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
- {
- int length = NTFS_GETU16(entry + 8);
- int nextblock = NTFS_GETU32(entry + length - 8);
- int error;
- if (!ntfs_entry_has_subnodes(entry)) {
- ntfs_error("illegal ntfs_descend calln");
- return 0;
- }
- error = ntfs_getdir_record(walk, nextblock);
- if (!error && walk->type == DIR_INSERT &&
- (walk->u.flags & ITERATE_SPLIT_DONE)) {
- /* Split has occurred. Adjust entry, insert new_entry. */
- NTFS_PUTU32(entry + length - 8, walk->newblock);
- /* Reset flags, as the current block might be split again. */
- walk->u.flags &= ~ITERATE_SPLIT_DONE;
- error = ntfs_dir_insert(walk, start, entry);
- }
- return error;
- }
- static int ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk, char* start,
- char *entry)
- {
- int retval = 0;
- int curpos = 0, destpos = 0;
- int length;
- if (walk->u.pos != 0) {
- if (ntfs_is_top(walk->u.pos))
- return 0;
- destpos = ntfs_pop(&walk->u.pos);
- }
- while (1) {
- if (walk->u.pos == 0) {
- if (ntfs_entry_has_subnodes(entry))
- ntfs_descend(walk, start, entry);
- else
- walk->u.pos = ntfs_top();
- if (ntfs_is_top(walk->u.pos) &&
- !ntfs_entry_is_used(entry))
- return 1;
- walk->u.pos = ntfs_push(walk->u.pos, curpos);
- return 1;
- }
- if (curpos == destpos) {
- if (!ntfs_is_top(walk->u.pos) &&
- ntfs_entry_has_subnodes(entry)) {
- retval = ntfs_descend(walk, start, entry);
- if (retval) {
- walk->u.pos = ntfs_push(walk->u.pos,
- curpos);
- return retval;
- }
- if (!ntfs_entry_is_used(entry))
- return 0;
- walk->u.pos = 0;
- }
- if (ntfs_entry_is_used(entry)) {
- retval = ntfs_copyresult(walk->result, entry);
- walk->u.pos = 0;
- } else {
- walk->u.pos = ntfs_top();
- return 0;
- }
- }
- curpos++;
- if (!ntfs_entry_is_used(entry))
- break;
- length = NTFS_GETU16(entry + 8);
- if (!length) {
- ntfs_error("infinite loopn");
- break;
- }
- entry += length;
- }
- return -1;
- }
-
- /* Iterate over a list of entries, either from an index block, or from the
- * index root.
- * If searching BY_POSITION, pop the top index from the position. If the
- * position stack is empty then, return the item at the index and set the
- * position to the next entry. If the position stack is not empty,
- * recursively proceed for subnodes. If the entry at the position is the
- * 'end of dir' entry, return 'not found' and the empty stack.
- * If searching BY_NAME, walk through the items until found or until
- * one item is collated after the requested item. In the former case, return
- * the result. In the latter case, recursively proceed to the subnodes.
- * If 'end of dir' is reached, the name is not in the directory */
- static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
- {
- int length;
- int cmp;
- if (walk->type == BY_POSITION)
- return ntfs_getdir_iterate_byposition(walk, start, entry);
- do {
- /* If the current entry is a real one, compare with the
- * requested item. If the current entry is the last item, it
- * is always larger than the requested item. */
- cmp = ntfs_entry_is_used(entry) ?
- ntfs_my_strcmp(walk,entry) : -1;
- switch (walk->type) {
- case BY_NAME:
- switch (cmp) {
- case -1:
- return ntfs_entry_has_subnodes(entry) ?
- ntfs_descend(walk, start, entry) : 0;
- case 0:
- return ntfs_copyresult(walk->result, entry);
- case 1:
- break;
- }
- break;
- case DIR_INSERT:
- switch (cmp) {
- case -1:
- return ntfs_entry_has_subnodes(entry) ?
- ntfs_descend(walk, start, entry) :
- ntfs_dir_insert(walk, start, entry);
- case 0:
- return -EEXIST;
- case 1:
- break;
- }
- break;
- default:
- ntfs_error("TODOn"); /* FIXME: ? */
- }
- if (!ntfs_entry_is_used(entry))
- break;
- length = NTFS_GETU16(entry + 8);
- if (!length) {
- ntfs_error("infinite loopn");
- break;
- }
- entry += length;
- } while (1);
- return 0;
- }
- /* Tree walking is done using position numbers. The following numbers have a
- * special meaning:
- * 0 start (.)
- * -1 no more entries
- * -2 ..
- * All other numbers encode sequences of indices. The sequence a, b, c is
- * encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
- * first few integers are encoded as follows:
- * 0: 0000 1: 0010 2: 0100 3: 0110
- * 4: 1000 5: 1010 6: 1100 stop: 1110
- * 7: 000001 8: 000101 9: 001001 10: 001101
- * The least significant bits give the width of this encoding, the other bits
- * encode the value, starting from the first value of the interval.
- * tag width first value last value
- * 0 3 0 6
- * 01 4 7 22
- * 011 5 23 54
- * 0111 6 55 119
- * More values are hopefully not needed, as the file position has currently
- * 64 bits in total. */
- /* Find an entry in the directory. Return 0 if not found, otherwise copy the
- * entry to the result buffer. */
- int ntfs_getdir(ntfs_iterate_s *walk)
- {
- int length = walk->dir->vol->mft_record_size;
- int retval, error;
- /* Start at the index root. */
- char *root = ntfs_malloc(length);
- ntfs_io io;
- if (!root)
- return -ENOMEM;
- io.fn_put = ntfs_put;
- io.param = root;
- io.size = length;
- error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_root, I30,
- 0, &io);
- if (error) {
- ntfs_error("Not a directoryn");
- return 0;
- }
- walk->block = -1;
- /* FIXME: Move these to walk. */
- walk->dir->u.index.recordsize = NTFS_GETU32(root + 0x8);
- walk->dir->u.index.clusters_per_record = NTFS_GETU32(root + 0xC);
- /* FIXME: Consistency check. */
- /* Skip header. */
- retval = ntfs_getdir_iterate(walk, root, root + 0x20);
- ntfs_free(root);
- return retval;
- }
- /* Find an entry in the directory by its position stack. Iteration starts
- * if the stack is 0, in which case the position is set to the first item
- * in the directory. If the position is nonzero, return the item at the
- * position and change the position to the next item. The position is -1
- * if there are no more items. */
- int ntfs_getdir_byposition(ntfs_iterate_s *walk)
- {
- walk->type = BY_POSITION;
- return ntfs_getdir(walk);
- }
- /* Find an entry in the directory by its name. Return 0 if not found. */
- int ntfs_getdir_byname(ntfs_iterate_s *walk)
- {
- walk->type = BY_NAME;
- return ntfs_getdir(walk);
- }
- int ntfs_getdir_unsorted(ntfs_inode *ino, u32 *p_high, u32 *p_low,
- int (*cb)(ntfs_u8 *, void *), void *param)
- {
- s64 ib_ofs;
- char *buf = 0, *entry = 0;
- ntfs_attribute *attr;
- ntfs_volume *vol;
- int byte, bit, err = 0;
- u32 start, finish, ibs, max_size;
- ntfs_io io;
- u8 ibs_bits;
- if (!ino) {
- ntfs_error(__FUNCTION__ "(): No inode! Returning -EINVAL.n");
- return -EINVAL;
- }
- vol = ino->vol;
- if (!vol) {
- ntfs_error(__FUNCTION__ "(): Inode 0x%lx has no volume. "
- "Returning -EINVAL.n", ino->i_number);
- return -EINVAL;
- }
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 1: Entering for "
- "inode 0x%lx, p_high = 0x%x, p_low = 0x%x.n",
- ino->i_number, *p_high, *p_low);
- if (!*p_high) {
- /* We are still in the index root. */
- buf = ntfs_malloc(io.size = vol->mft_record_size);
- if (!buf)
- return -ENOMEM;
- io.fn_put = ntfs_put;
- io.param = buf;
- err = ntfs_read_attr(ino, vol->at_index_root, I30, 0, &io);
- if (err || !io.size)
- goto read_err_ret;
- ino->u.index.recordsize = ibs = NTFS_GETU32(buf + 0x8);
- ino->u.index.clusters_per_record = NTFS_GETU32(buf + 0xC);
- entry = buf + 0x20;
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 2: In index "
- "root.n");
- ibs_bits = ffs(ibs) - 1;
- /* Compensate for faked "." and "..". */
- start = 2;
- } else { /* We are in an index record. */
- io.size = ibs = ino->u.index.recordsize;
- buf = ntfs_malloc(ibs);
- if (!buf)
- return -ENOMEM;
- ibs_bits = ffs(ibs) - 1;
- io.fn_put = ntfs_put;
- io.param = buf;
- /*
- * 0 is index root, index allocation starts at 1 and works in
- * units of index block size (ibs).
- */
- ib_ofs = (s64)(*p_high - 1) << ibs_bits;
- err = ntfs_read_attr(ino, vol->at_index_allocation, I30, ib_ofs,
- &io);
- if (err || io.size != ibs)
- goto read_err_ret;
- if (!ntfs_check_index_record(ino, buf)) {
- ntfs_error(__FUNCTION__ "(): Index block 0x%x is not "
- "an index record. Returning "
- "-ENOTDIR.n", *p_high - 1);
- ntfs_free(buf);
- return -ENOTDIR;
- }
- entry = buf + 0x18 + NTFS_GETU16(buf + 0x18);
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 3: In index "
- "allocation.n");
- start = 0;
- }
- /* Process the entries. */
- finish = *p_low;
- for (; entry < (buf + ibs) && ntfs_entry_is_used(entry);
- entry += NTFS_GETU16(entry + 8)) {
- if (start < finish) {
- /* Skip entries that were already processed. */
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 4: "
- "Skipping already processed entry "
- "p_high 0x%x, p_low 0x%x.n", *p_high,
- start);
- start++;
- continue;
- }
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 5: "
- "Processing entry p_high 0x%x, p_low 0x%x.n",
- *p_high, *p_low);
- if ((err = cb(entry, param))) {
- /* filldir signalled us to stop. */
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): "
- "Unsorted 6: cb returned %i, "
- "returning 0, p_high 0x%x, p_low 0x%x."
- "n", *p_high, *p_low);
- ntfs_free(buf);
- return 0;
- }
- ++*p_low;
- }
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 7: After processing "
- "entries, p_high 0x%x, p_low 0x%x.n", *p_high, *p_low);
- /* We have to locate the next record. */
- ntfs_free(buf);
- buf = 0;
- *p_low = 0;
- attr = ntfs_find_attr(ino, vol->at_bitmap, I30);
- if (!attr) {
- /* Directory does not have index bitmap and index allocation. */
- *p_high = 0x7fff;
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 8: No index "
- "allocation. Returning 0, p_high 0x7fff, "
- "p_low 0x0.n");
- return 0;
- }
- max_size = attr->size;
- if (max_size > 0x7fff >> 3) {
- ntfs_error(__FUNCTION__ "(): Directory too large. Visible "
- "length is truncated.n");
- max_size = 0x7fff >> 3;
- }
- buf = ntfs_malloc(max_size);
- if (!buf)
- return -ENOMEM;
- io.param = buf;
- io.size = max_size;
- err = ntfs_read_attr(ino, vol->at_bitmap, I30, 0, &io);
- if (err || io.size != max_size)
- goto read_err_ret;
- attr = ntfs_find_attr(ino, vol->at_index_allocation, I30);
- if (!attr) {
- ntfs_free(buf);
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 9: Find "
- "attr failed. Returning -EIO.n");
- return -EIO;
- }
- if (attr->resident) {
- ntfs_free(buf);
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 9.5: IA is "
- "resident. Not allowed. Returning EINVAL.n");
- return -EINVAL;
- }
- /* Loop while going through non-allocated index records. */
- max_size <<= 3;
- while (1) {
- if (++*p_high >= 0x7fff) {
- ntfs_error(__FUNCTION__ "(): Unsorted 10: Directory "
- "inode 0x%lx overflowed the maximum "
- "number of index allocation buffers "
- "the driver can cope with. Pretending "
- "to be at end of directory.n",
- ino->i_number);
- goto fake_eod;
- }
- if (*p_high > max_size || (s64)*p_high << ibs_bits >
- attr->initialized) {
- fake_eod:
- /* No more index records. */
- *p_high = 0x7fff;
- *p_low = 0;
- ntfs_free(buf);
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted "
- "10.5: No more index records. "
- "Returning 0, p_high 0x7fff, p_low "
- "0.n");
- return 0;
- }
- byte = (ntfs_cluster_t)(*p_high - 1);
- bit = 1 << (byte & 7);
- byte >>= 3;
- if ((buf[byte] & bit))
- break;
- };
- ntfs_debug(DEBUG_DIR3, __FUNCTION__ "(): Unsorted 11: Done. "
- "Returning 0, p_high 0x%x, p_low 0x%x.n", *p_high,
- *p_low);
- ntfs_free(buf);
- return 0;
- read_err_ret:
- if (!err)
- err = -EIO;
- ntfs_error(__FUNCTION__ "(): Read failed. Returning error code %i.n",
- err);
- ntfs_free(buf);
- return err;
- }
- int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
- {
- ntfs_iterate_s walk;
- int nsize, esize;
- ntfs_u8* entry, *ndata;
- int error;
- walk.type = DIR_INSERT;
- walk.dir = dir;
- walk.u.flags = 0;
- nsize = name->size;
- ndata = name->d.data;
- walk.name = (ntfs_u16*)(ndata + 0x42);
- walk.namelen = NTFS_GETU8(ndata + 0x40);
- walk.new_entry_size = esize = (nsize + 0x10 + 7) & ~7;
- walk.new_entry = entry = ntfs_malloc(esize);
- if (!entry)
- return -ENOMEM;
- NTFS_PUTINUM(entry, new);
- NTFS_PUTU16(entry + 0x8, esize); /* Size of entry. */
- NTFS_PUTU16(entry + 0xA, nsize); /* Size of original name attribute. */
- NTFS_PUTU16(entry + 0xC, 0); /* Flags. */
- NTFS_PUTU16(entry + 0xE, 0); /* Reserved. */
- ntfs_memcpy(entry + 0x10, ndata, nsize);
- ntfs_bzero(entry + 0x10 + nsize, esize - 0x10 - nsize);
- error = ntfs_getdir(&walk);
- if (walk.new_entry)
- ntfs_free(walk.new_entry);
- return error;
- }
- #if 0
- int ntfs_dir_add1(ntfs_inode *dir, const char* name, int namelen,
- ntfs_inode *ino)
- {
- ntfs_iterate_s walk;
- int error;
- int nsize;
- char *entry;
- ntfs_attribute *name_attr;
- error = ntfs_decodeuni(dir->vol, name, namelen, &walk.name,
- &walk.namelen);
- if (error)
- return error;
- /* FIXME: Set flags. */
- walk.type = DIR_INSERT;
- walk.dir = dir;
- /* walk.new = ino; */
- /* Prepare new entry. */
- /* Round up to a multiple of 8. */
- walk.new_entry_size = nsize = ((0x52 + 2 * walk.namelen + 7) / 8) * 8;
- walk.new_entry = entry = ntfs_malloc(nsize);
- if (!entry)
- return -ENOMEM;
- ntfs_bzero(entry, nsize);
- NTFS_PUTINUM(entry, ino);
- NTFS_PUTU16(entry + 8, nsize);
- NTFS_PUTU16(entry + 0xA, 0x42 + 2 * namelen); /* FIXME: Size of name
- * attribute. */
- NTFS_PUTU32(entry + 0xC, 0); /* FIXME: D-F? */
- name_attr = ntfs_find_attr(ino, vol->at_file_name, 0);
- /* FIXME: multiple names */
- if (!name_attr || !name_attr->resident)
- return -EIDRM;
- /* Directory, file stamps, sizes, filename. */
- ntfs_memcpy(entry + 0x10, name_attr->d.data, 0x42 + 2 * namelen);
- error = ntfs_getdir(&walk);
- ntfs_free(walk.name);
- return error;
- }
- #endif
- /* Fills out and creates an INDEX_ROOT attribute. */
- int ntfs_add_index_root(ntfs_inode *ino, int type)
- {
- ntfs_attribute *da;
- ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry. */
- char name[10];
- NTFS_PUTU32(data, type);
- /* Collation rule. 1 == COLLATION_FILENAME */
- NTFS_PUTU32(data + 4, 1);
- NTFS_PUTU32(data + 8, ino->vol->index_record_size);
- NTFS_PUTU32(data + 0xC, ino->vol->index_clusters_per_record);
- /* Byte offset to first INDEX_ENTRY. */
- NTFS_PUTU32(data + 0x10, 0x10);
- /* Size of entries, including header. */
- NTFS_PUTU32(data + 0x14, 0x20);
- NTFS_PUTU32(data + 0x18, 0x20);
- /* No index allocation, yet. */
- NTFS_PUTU32(data + 0x1C, 0);
- /* Add last entry. */
- /* Indexed MFT record. */
- NTFS_PUTU64(data + 0x20, 0);
- /* Size of entry. */
- NTFS_PUTU32(data + 0x28, 0x10);
- /* Flags: Last entry, no child nodes. */
- NTFS_PUTU32(data + 0x2C, 2);
- /* Compute name. */
- ntfs_indexname(name, type);
- return ntfs_create_attr(ino, ino->vol->at_index_root, name,
- data, sizeof(data), &da);
- }
- int ntfs_mkdir(ntfs_inode *dir, const char *name, int namelen,
- ntfs_inode *result)
- {
- int error;
-
- error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
- if (error)
- goto out;
- error = ntfs_add_index_root(result, 0x30);
- if (error)
- goto out;
- /* Set directory bit. */
- result->attr[0x16] |= 2;
- error = ntfs_update_inode(dir);
- if (error)
- goto out;
- error = ntfs_update_inode(result);
- if (error)
- goto out;
- out:
- return error;
- }