Esempio n. 1
0
int ext2_check_dir_entry (const char * function, struct inode * dir,
			  struct ext2_dir_entry_2 * de,
			  struct buffer_head * bh,
			  unsigned long offset)
{
	const char * error_msg = NULL;

	if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1))
		error_msg = "rec_len is smaller than minimal";
	else if (le16_to_cpu(de->rec_len) % 4 != 0)
		error_msg = "rec_len % 4 != 0";
	else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(de->name_len))
		error_msg = "rec_len is too small for name_len";
	else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) >
		 dir->i_sb->s_blocksize)
		error_msg = "directory entry across blocks";
	else if (dir && le32_to_cpu(de->inode) > le32_to_cpu(dir->i_sb->u.ext2_sb.s_es->s_inodes_count))
		error_msg = "inode out of bounds";

	if (error_msg != NULL)
		ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - "
			    "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
			    dir->i_ino, error_msg, offset,
			    (unsigned long) le32_to_cpu(de->inode),
			    le16_to_cpu(de->rec_len), de->name_len);
	return error_msg == NULL ? 1 : 0;
}
Esempio n. 2
0
/*
 *	changed so that it confirms to ext2_check_dir_entry
 */
static int
ext2_dirbadentry(struct vnode *dp, struct ext2_dir_entry_2 *de,
		 int entryoffsetinblock)
{
	int	DIRBLKSIZ = VTOI(dp)->i_e2fs->s_blocksize;

        char * error_msg = NULL;

        if (de->rec_len < EXT2_DIR_REC_LEN(1))
                error_msg = "rec_len is smaller than minimal";
        else if (de->rec_len % 4 != 0)
                error_msg = "rec_len % 4 != 0";
        else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
                error_msg = "reclen is too small for name_len";
        else if (entryoffsetinblock + de->rec_len > DIRBLKSIZ)
                error_msg = "directory entry across blocks";
        /* else LATER
	     if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
                error_msg = "inode out of bounds";
	*/

        if (error_msg != NULL) {
                kprintf("bad directory entry: %s\n", error_msg);
                kprintf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
			entryoffsetinblock, (unsigned long)de->inode,
			de->rec_len, de->name_len);
        }
        return error_msg == NULL ? 0 : 1;
}
Esempio n. 3
0
int ext2_check_dir_entry (char * function, struct inode * dir,
			  struct ext2_dir_entry * de, struct buffer_head * bh,
			  unsigned long offset)
{
	char * error_msg = NULL;

	if (de->rec_len < EXT2_DIR_REC_LEN(1))
		error_msg = "rec_len is smaller than minimal";
	else if (de->rec_len % 4 != 0)
		error_msg = "rec_len % 4 != 0";
	else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
		error_msg = "rec_len is too small for name_len";
	else if (dir && ((char *) de - bh->b_data) + de->rec_len >
		 dir->i_sb->s_blocksize)
		error_msg = "directory entry across blocks";
	else if (dir && de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
		error_msg = "inode out of bounds";

	if (error_msg != NULL)
		ext2_error (dir->i_sb, function, "bad directory entry: %s\n"
			    "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
			    error_msg, offset, de->inode, de->rec_len,
			    de->name_len);
	return error_msg == NULL ? 1 : 0;
}
Esempio n. 4
0
static int link_proc(struct ext2_dir_entry *dirent,
		     int	offset,
		     int	blocksize,
		     char	*buf,
		     void	*priv_data)
{
	struct link_struct *ls = (struct link_struct *) priv_data;
	struct ext2_dir_entry *next;
	int rec_len, min_rec_len;
	int ret = 0;

	rec_len = EXT2_DIR_REC_LEN(ls->namelen);

	/*
	 * See if the following directory entry (if any) is unused;
	 * if so, absorb it into this one.
	 */
	next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
	if ((offset + dirent->rec_len < blocksize - 8) &&
	    (next->inode == 0) &&
	    (offset + dirent->rec_len + next->rec_len <= blocksize)) {
		dirent->rec_len += next->rec_len;
		ret = DIRENT_CHANGED;
	}

	/*
	 * If the directory entry is used, see if we can split the
	 * directory entry to make room for the new name.  If so,
	 * truncate it and return.
	 */
	if (dirent->inode) {
		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
		if (dirent->rec_len < (min_rec_len + rec_len))
			return ret;
		rec_len = dirent->rec_len - min_rec_len;
		dirent->rec_len = min_rec_len;
		next = (struct ext2_dir_entry *) (buf + offset +
						  dirent->rec_len);
		next->inode = 0;
		next->name_len = 0;
		next->rec_len = rec_len;
		return DIRENT_CHANGED;
	}

	/*
	 * If we get this far, then the directory entry is not used.
	 * See if we can fit the request entry in.  If so, do it.
	 */
	if (dirent->rec_len < rec_len)
		return ret;
	dirent->inode = ls->inode;
	dirent->name_len = ls->namelen;
	strncpy(dirent->name, ls->name, ls->namelen);
	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
		dirent->name_len |= (ls->flags & 0x7) << 8;

	ls->done++;
	return DIRENT_ABORT|DIRENT_CHANGED;
}
Esempio n. 5
0
static uint32_t
ext2_htree_root_limit(struct inode *ip, int len)
{
	uint32_t space;

	space = ip->i_e2fs->e2fs_bsize - EXT2_DIR_REC_LEN(1) -
	    EXT2_DIR_REC_LEN(2) - len;
	return (space / sizeof(struct ext2fs_htree_entry));
}
Esempio n. 6
0
/*
 * Append an entry to the end of the directory block.
 */
static void
ext2_append_entry(char *block, uint32_t blksize,
		  struct ext2fs_direct_2 *last_entry,
		  struct ext2fs_direct_2 *new_entry)
{
	uint16_t entry_len;

	entry_len = EXT2_DIR_REC_LEN(last_entry->e2d_namlen);
	last_entry->e2d_reclen = entry_len;
	last_entry = (struct ext2fs_direct_2 *)((char *)last_entry + entry_len);
	new_entry->e2d_reclen = block + blksize - (char *)last_entry;
	memcpy(last_entry, new_entry, EXT2_DIR_REC_LEN(new_entry->e2d_namlen));
}
Esempio n. 7
0
/*
 * Create new directory block
 */
errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
			       ext2_ino_t parent_ino, char **block)
{
	struct ext2_dir_entry	*dir = NULL;
	errcode_t		retval;
	char			*buf;
	int			rec_len;
	int			filetype = 0;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	retval = ext2fs_get_mem(fs->blocksize, &buf);
	if (retval)
		return retval;
	memset(buf, 0, fs->blocksize);
	dir = (struct ext2_dir_entry *) buf;

	retval = ext2fs_set_rec_len(fs, fs->blocksize, dir);
	if (retval)
		return retval;

	if (dir_ino) {
		if (fs->super->s_feature_incompat &
		    EXT2_FEATURE_INCOMPAT_FILETYPE)
			filetype = EXT2_FT_DIR << 8;
		/*
		 * Set up entry for '.'
		 */
		dir->inode = dir_ino;
		dir->name_len = 1 | filetype;
		dir->name[0] = '.';
		rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1);
		dir->rec_len = EXT2_DIR_REC_LEN(1);

		/*
		 * Set up entry for '..'
		 */
		dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
		retval = ext2fs_set_rec_len(fs, rec_len, dir);
		if (retval)
			return retval;
		dir->inode = parent_ino;
		dir->name_len = 2 | filetype;
		dir->name[0] = '.';
		dir->name[1] = '.';

	}
	*block = buf;
	return 0;
}
Esempio n. 8
0
/*
 * Create new directory block
 */
bool ext2_new_dir_block(PEXT2_FILESYS fs, ULONG dir_ino,
                   ULONG parent_ino, char **block)
{
    PEXT2_DIR_ENTRY dir = NULL;
    char        *buf;
    int         rec_len;
    int         filetype = 0;

    buf = (char *)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, fs->blocksize);
    if (!buf)
        return false;

    dir = (PEXT2_DIR_ENTRY) buf;
    dir->rec_len = fs->blocksize;

    if (dir_ino)
    {
        if (fs->ext2_sb->s_feature_incompat &
            EXT2_FEATURE_INCOMPAT_FILETYPE)
            filetype = EXT2_FT_DIR << 8;
        /*
         * Set up entry for '.'
         */
        dir->inode = dir_ino;
        dir->name_len = 1 | filetype;
        dir->name[0] = '.';
        rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1);
        dir->rec_len = EXT2_DIR_REC_LEN(1);

        /*
         * Set up entry for '..'
         */
        dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
        dir->rec_len = rec_len;
        dir->inode = parent_ino;
        dir->name_len = 2 | filetype;
        dir->name[0] = '.';
        dir->name[1] = '.';
    }

    *block = buf;

    return true;
}
Esempio n. 9
0
static uint32_t
ext2_htree_node_limit(struct inode *ip)
{
	struct m_ext2fs *fs;
	uint32_t space;

	fs = ip->i_e2fs;
	space = fs->e2fs_bsize - EXT2_DIR_REC_LEN(0);

	return (space / sizeof(struct ext2fs_htree_entry));
}
Esempio n. 10
0
/*
 * Create new directory block
 */
errcode_t ext2fs_new_dir_block(ext2_filsys fs, ino_t dir_ino, ino_t parent_ino,
			       char **block)
{
	char	*buf;
	struct ext2_dir_entry *dir = NULL;
	int	rec_len;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	buf = malloc(fs->blocksize);
	if (!buf)
		return ENOMEM;
	memset(buf, 0, fs->blocksize);
	dir = (struct ext2_dir_entry *) buf;
	dir->rec_len = fs->blocksize;

	if (dir_ino) {
		/*
		 * Set up entry for '.'
		 */
		dir->inode = dir_ino;
		dir->name_len = 1;
		dir->name[0] = '.';
		rec_len = dir->rec_len - EXT2_DIR_REC_LEN(dir->name_len);
		dir->rec_len = EXT2_DIR_REC_LEN(dir->name_len);

		/*
		 * Set up entry for '..'
		 */
		dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
		dir->rec_len = rec_len;
		dir->inode = parent_ino;
		dir->name_len = 2;
		dir->name[0] = '.';
		dir->name[1] = '.';
		
	}
	*block = buf;
	return 0;
}
Esempio n. 11
0
static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
        char *bbuf, char *ibuf, int size)
{
    struct ext2_dir_entry *dir, *dir2;
    struct ext2_dir_entry_tail *t;
    errcode_t retval;
    unsigned int offset, rec_len;
    int csum_size = 0;
    int filetype = 0;

    if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
                                   EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
        csum_size = sizeof(struct ext2_dir_entry_tail);

    /* Create '.' and '..' */
    if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
                                  EXT2_FEATURE_INCOMPAT_FILETYPE))
        filetype = EXT2_FT_DIR;

    /*
     * Set up entry for '.'
     */
    dir = (struct ext2_dir_entry *) bbuf;
    dir->inode = ino;
    ext2fs_dirent_set_name_len(dir, 1);
    ext2fs_dirent_set_file_type(dir, filetype);
    dir->name[0] = '.';
    rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
    dir->rec_len = EXT2_DIR_REC_LEN(1);

    /*
     * Set up entry for '..'
     */
    dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
    dir->rec_len = EXT2_DIR_REC_LEN(2);
    dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
    ext2fs_dirent_set_name_len(dir, 2);
    ext2fs_dirent_set_file_type(dir, filetype);
    dir->name[0] = '.';
    dir->name[1] = '.';

    /*
     * Ajust the last rec_len
     */
    offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
    dir = (struct ext2_dir_entry *) (bbuf + offset);
    memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
           size - EXT4_INLINE_DATA_DOTDOT_SIZE);
    size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) -
            EXT4_INLINE_DATA_DOTDOT_SIZE;

    do {
        dir2 = dir;
        retval = ext2fs_get_rec_len(fs, dir, &rec_len);
        if (retval)
            goto err;
        offset += rec_len;
        dir = (struct ext2_dir_entry *) (bbuf + offset);
    } while (offset < size);
    rec_len += fs->blocksize - csum_size - offset;
    retval = ext2fs_set_rec_len(fs, rec_len, dir2);
    if (retval)
        goto err;

    if (csum_size) {
        t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
        ext2fs_initialize_dirent_tail(fs, t);
    }

err:
    return retval;
}
Esempio n. 12
0
int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
                                   void *priv_data)
{
    struct dir_context *ctx;
    struct ext2_inode inode;
    struct ext2_dir_entry dirent;
    struct ext2_inline_data data;
    int ret = BLOCK_ABORT;
    e2_blkcnt_t blockcnt = 0;
    char *old_buf;
    unsigned int old_buflen;
    int old_flags;

    ctx = (struct dir_context *)priv_data;
    old_buf = ctx->buf;
    old_buflen = ctx->buflen;
    old_flags = ctx->flags;
    ctx->flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA;

    ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
    if (ctx->errcode)
        goto out;

    if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
        ctx->errcode = EXT2_ET_NO_INLINE_DATA;
        goto out;
    }

    if (!LINUX_S_ISDIR(inode.i_mode)) {
        ctx->errcode = EXT2_ET_NO_DIRECTORY;
        goto out;
    }
    ret = 0;

    /* we first check '.' and '..' dir */
    dirent.inode = ino;
    dirent.name_len = 1;
    ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent);
    dirent.name[0] = '.';
    dirent.name[1] = '\0';
    ctx->buf = (char *)&dirent;
    ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_ABORT)
        goto out;

    dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
    dirent.name_len = 2;
    ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent);
    dirent.name[0] = '.';
    dirent.name[1] = '.';
    dirent.name[2] = '\0';
    ctx->buf = (char *)&dirent;
    ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_INLINE_DATA_CHANGED) {
        errcode_t err;

        inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
        err = ext2fs_write_inode(fs, ino, &inode);
        if (err)
            goto out;
        ret &= ~BLOCK_INLINE_DATA_CHANGED;
    }
    if (ret & BLOCK_ABORT)
        goto out;

    ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
    ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
#ifdef WORDS_BIGENDIAN
    ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
    if (ctx->errcode) {
        ret |= BLOCK_ABORT;
        goto out;
    }
#endif
    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_INLINE_DATA_CHANGED) {
#ifdef WORDS_BIGENDIAN
        ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
                                               ctx->buflen, 0);
        if (ctx->errcode) {
            ret |= BLOCK_ABORT;
            goto out;
        }
#endif
        ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
        if (ctx->errcode)
            ret |= BLOCK_ABORT;
        ret &= ~BLOCK_INLINE_DATA_CHANGED;
    }
    if (ret & BLOCK_ABORT)
        goto out;

    data.fs = fs;
    data.ino = ino;
    ctx->errcode = ext2fs_inline_data_ea_get(&data);
    if (ctx->errcode) {
        ret |= BLOCK_ABORT;
        goto out;
    }
    if (data.ea_size <= 0)
        goto out1;

    ctx->buf = data.ea_data;
    ctx->buflen = data.ea_size;
#ifdef WORDS_BIGENDIAN
    ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
    if (ctx->errcode) {
        ret |= BLOCK_ABORT;
        goto out1;
    }
#endif

    ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
    if (ret & BLOCK_INLINE_DATA_CHANGED) {
#ifdef WORDS_BIGENDIAN
        ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
                                               ctx->buflen, 0);
        if (ctx->errcode) {
            ret |= BLOCK_ABORT;
            goto out1;
        }
#endif
        ctx->errcode = ext2fs_inline_data_ea_set(&data);
        if (ctx->errcode)
            ret |= BLOCK_ABORT;
    }

out1:
    ext2fs_free_mem(&data.ea_data);
out:
    ctx->buf = old_buf;
    ctx->buflen = old_buflen;
    ctx->flags = old_flags;
    ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
    return ret;
}
Esempio n. 13
0
/*
 * Move half of entries from the old directory block to the new one.
 */
static int
ext2_htree_split_dirblock(char *block1, char *block2, uint32_t blksize,
			  uint32_t *hash_seed, uint8_t hash_version,
			  uint32_t *split_hash, struct ext2fs_direct_2 *entry)
{
	int entry_cnt = 0;
	int size = 0;
	int i, k;
	uint32_t offset;
	uint16_t entry_len = 0;
	uint32_t entry_hash;
	struct ext2fs_direct_2 *ep, *last;
	char *dest;
	struct ext2fs_htree_sort_entry *sort_info;

	ep = (struct ext2fs_direct_2 *)block1;
	dest = block2;
	sort_info = (struct ext2fs_htree_sort_entry *)
	    ((char *)block2 + blksize);

	/*
	 * Calculate name hash value for the entry which is to be added.
	 */
	ext2_htree_hash(entry->e2d_name, entry->e2d_namlen, hash_seed,
	    hash_version, &entry_hash, NULL);

	/*
	 * Fill in directory entry sort descriptors.
	 */
	while ((char *)ep < block1 + blksize) {
		if (ep->e2d_ino && ep->e2d_namlen) {
			entry_cnt++;
			sort_info--;
			sort_info->h_size = ep->e2d_reclen;
			sort_info->h_offset = (char *)ep - block1;
			ext2_htree_hash(ep->e2d_name, ep->e2d_namlen,
			    hash_seed, hash_version,
			    &sort_info->h_hash, NULL);
		}
		ep = (struct ext2fs_direct_2 *)
		    ((char *)ep + ep->e2d_reclen);
	}

	/*
	 * Sort directory entry descriptors by name hash value.
	 */
	qsort(sort_info, entry_cnt, sizeof(struct ext2fs_htree_sort_entry),
	    ext2_htree_cmp_sort_entry);

	/*
	 * Count the number of entries to move to directory block 2.
	 */
	for (i = entry_cnt - 1; i >= 0; i--) {
		if (sort_info[i].h_size + size > blksize / 2)
			break;
		size += sort_info[i].h_size;
	}

	*split_hash = sort_info[i + 1].h_hash;

	/*
	 * Set collision bit.
	 */
	if (*split_hash == sort_info[i].h_hash)
		*split_hash += 1;

	/*
	 * Move half of directory entries from block 1 to block 2.
	 */
	for (k = i + 1; k < entry_cnt; k++) {
		ep = (struct ext2fs_direct_2 *)((char *)block1 +
		    sort_info[k].h_offset);
		entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen);
		memcpy(dest, ep, entry_len);
		((struct ext2fs_direct_2 *)dest)->e2d_reclen = entry_len;
		/* Mark directory entry as unused. */
		ep->e2d_ino = 0;
		dest += entry_len;
	}
	dest -= entry_len;

	/* Shrink directory entries in block 1. */
	last = (struct ext2fs_direct_2 *)block1;
	entry_len = EXT2_DIR_REC_LEN(last->e2d_namlen);
	for (offset = last->e2d_reclen; offset < blksize; ) {
		ep = (struct ext2fs_direct_2 *)(block1 + offset);
		offset += ep->e2d_reclen;
		if (last->e2d_ino) {
			/* Trim the existing slot */
			last->e2d_reclen = entry_len;
			last = (struct ext2fs_direct_2 *)
			   ((char *)last + entry_len);
		}
		entry_len = EXT2_DIR_REC_LEN(ep->e2d_namlen);
		memcpy((void *)last, (void *)ep, entry_len);
	}

	if (entry_hash >= *split_hash) {
		/* Add entry to block 2. */
		ext2_append_entry(block2, blksize,
		    (struct ext2fs_direct_2 *)dest, entry);

		/* Adjust length field of last entry of block 1. */
		last->e2d_reclen = block1 + blksize - (char *)last;
	} else {
		/* Add entry to block 1. */
		ext2_append_entry(block1, blksize, last, entry);

		/* Adjust length field of last entry of block 2. */
		((struct ext2fs_direct_2 *)dest)->e2d_reclen =
		    block2 + blksize - dest;
	}

	return (0);
}
Esempio n. 14
0
static int ext2_readdir(struct file * filp,
			 void * dirent, filldir_t filldir)
{
	int error = 0;
	unsigned long offset, blk;
	int i, num, stored;
	struct buffer_head * bh, * tmp, * bha[16];
	struct ext2_dir_entry_2 * de;
	struct super_block * sb;
	int err;
	struct inode *inode = filp->f_dentry->d_inode;

	if (!inode || !S_ISDIR(inode->i_mode))
		return -EBADF;
	sb = inode->i_sb;

	stored = 0;
	bh = NULL;
	offset = filp->f_pos & (sb->s_blocksize - 1);

	while (!error && !stored && filp->f_pos < inode->i_size) {
		blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
		bh = ext2_bread (inode, blk, 0, &err);
		if (!bh) {
			ext2_error (sb, "ext2_readdir",
				    "directory #%lu contains a hole at offset %lu",
				    inode->i_ino, (unsigned long)filp->f_pos);
			filp->f_pos += sb->s_blocksize - offset;
			continue;
		}

		/*
		 * Do the readahead
		 */
		if (!offset) {
			for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
			     i > 0; i--) {
				tmp = ext2_getblk (inode, ++blk, 0, &err);
				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
					bha[num++] = tmp;
				else
					brelse (tmp);
			}
			if (num) {
				ll_rw_block (READA, num, bha);
				for (i = 0; i < num; i++)
					brelse (bha[i]);
			}
		}
		
revalidate:
		/* If the dir block has changed since the last call to
		 * readdir(2), then we might be pointing to an invalid
		 * dirent right now.  Scan from the start of the block
		 * to make sure. */
		if (filp->f_version != inode->i_version) {
			for (i = 0; i < sb->s_blocksize && i < offset; ) {
				de = (struct ext2_dir_entry_2 *) 
					(bh->b_data + i);
				/* It's too expensive to do a full
				 * dirent test each time round this
				 * loop, but we do have to test at
				 * least that it is non-zero.  A
				 * failure will be detected in the
				 * dirent test below. */
				if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1))
					break;
				i += le16_to_cpu(de->rec_len);
			}
			offset = i;
			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
				| offset;
			filp->f_version = inode->i_version;
		}
		
		while (!error && filp->f_pos < inode->i_size 
		       && offset < sb->s_blocksize) {
			de = (struct ext2_dir_entry_2 *) (bh->b_data + offset);
			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
						   bh, offset)) {
				/* On error, skip the f_pos to the
                                   next block. */
				filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
					      + sb->s_blocksize;
				brelse (bh);
				return stored;
			}
			offset += le16_to_cpu(de->rec_len);
			if (le32_to_cpu(de->inode)) {
				/* We might block in the next section
				 * if the data destination is
				 * currently swapped out.  So, use a
				 * version stamp to detect whether or
				 * not the directory has been modified
				 * during the copy operation.
				 */
				unsigned long version = inode->i_version;

				error = filldir(dirent, de->name,
						de->name_len,
						filp->f_pos, le32_to_cpu(de->inode));
				if (error)
					break;
				if (version != inode->i_version)
					goto revalidate;
				stored ++;
			}
			filp->f_pos += le16_to_cpu(de->rec_len);
		}
		offset = 0;
		brelse (bh);
	}
	UPDATE_ATIME(inode);
	return 0;
}
Esempio n. 15
0
/*
 * Create an HTree index for a directory
 */
int
ext2_htree_create_index(struct vnode *vp, struct componentname *cnp,
			struct ext2fs_direct_2 *new_entry)
{
	struct buf *bp = NULL;
	struct inode *dp;
	struct ext2fs *fs;
	struct m_ext2fs *m_fs;
	struct ext2fs_direct_2 *ep, *dotdot;
	struct ext2fs_htree_root *root;
	struct ext2fs_htree_lookup_info info;
	uint32_t blksize, dirlen, split_hash;
	uint8_t hash_version;
	char *buf1 = NULL;
	char *buf2 = NULL;
	int error = 0;

	dp = VTOI(vp);
	fs = dp->i_e2fs->e2fs;
	m_fs = dp->i_e2fs;
	blksize = m_fs->e2fs_bsize;

	buf1 = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);
	buf2 = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO);

	if ((error = ext2_blkatoff(vp, 0, NULL, &bp)) != 0)
		goto out;

	root = (struct ext2fs_htree_root *)bp->b_data;
	dotdot = (struct ext2fs_direct_2 *)((char *)&(root->h_dotdot));
	ep = (struct ext2fs_direct_2 *)((char *)dotdot + dotdot->e2d_reclen);
	dirlen = (char *)root + blksize - (char *)ep;
	memcpy(buf1, ep, dirlen);
	ep = (struct ext2fs_direct_2 *)buf1;
	while ((char *)ep < buf1 + dirlen)
		ep = (struct ext2fs_direct_2 *)
		    ((char *)ep + ep->e2d_reclen);
	ep->e2d_reclen = buf1 + blksize - (char *)ep;

	dp->i_flag |= IN_E4INDEX;

	/*
	 * Initialize index root.
	 */
	dotdot->e2d_reclen = blksize - EXT2_DIR_REC_LEN(1);
	memset(&root->h_info, 0, sizeof(root->h_info));
	root->h_info.h_hash_version = fs->e3fs_def_hash_version;
	root->h_info.h_info_len = sizeof(root->h_info);
	ext2_htree_set_block(root->h_entries, 1);
	ext2_htree_set_count(root->h_entries, 1);
	ext2_htree_set_limit(root->h_entries,
	    ext2_htree_root_limit(dp, sizeof(root->h_info)));

	memset(&info, 0, sizeof(info));
	info.h_levels_num = 1;
	info.h_levels[0].h_entries = root->h_entries;
	info.h_levels[0].h_entry = root->h_entries;

	hash_version = root->h_info.h_hash_version;
	if (hash_version <= EXT2_HTREE_TEA)
		hash_version += m_fs->e2fs_uhash;
	ext2_htree_split_dirblock(buf1, buf2, blksize, fs->e3fs_hash_seed,
	    hash_version, &split_hash, new_entry);
	ext2_htree_insert_entry(&info, split_hash, 2);

	/*
	 * Write directory block 0.
	 */
	if (DOINGASYNC(vp)) {
		bdwrite(bp);
		error = 0;
	} else {
		error = bwrite(bp);
	}
	dp->i_flag |= IN_CHANGE | IN_UPDATE;
	if (error)
		goto out;

	/*
	 * Write directory block 1.
	 */
	error = ext2_htree_append_block(vp, buf1, cnp, blksize);
	if (error)
		goto out1;

	/*
	 * Write directory block 2.
	 */
	error = ext2_htree_append_block(vp, buf2, cnp, blksize);

	free(buf1, M_TEMP);
	free(buf2, M_TEMP);
	return (error);
out:
	if (bp != NULL)
		brelse(bp);
out1:
	free(buf1, M_TEMP);
	free(buf2, M_TEMP);
	return (error);
}
Esempio n. 16
0
static errcode_t copy_dir_entries(ext2_filsys fs,
				  struct fill_dir_struct *fd,
				  struct out_dir *outdir)
{
	errcode_t		retval;
	char			*block_start;
	struct hash_entry 	*ent;
	struct ext2_dir_entry	*dirent;
	int			i, rec_len, left;
	ext2_dirhash_t		prev_hash;
	int			offset;
	
	outdir->max = 0;
	retval = alloc_size_dir(fs, outdir,
				(fd->dir_size / fs->blocksize) + 2);
	if (retval)
		return retval;
	outdir->num = fd->compress ? 0 : 1;
	offset = 0;
	outdir->hashes[0] = 0;
	prev_hash = 1;
	if ((retval = get_next_block(fs, outdir, &block_start)))
		return retval;
	dirent = (struct ext2_dir_entry *) block_start;
	left = fs->blocksize;
	for (i=0; i < fd->num_array; i++) {
		ent = fd->harray + i;
		if (ent->dir->inode == 0)
			continue;
		rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
		if (rec_len > left) {
			if (left)
				dirent->rec_len += left;
			if ((retval = get_next_block(fs, outdir,
						      &block_start)))
				return retval;
			offset = 0;
		}
		left = fs->blocksize - offset;
		dirent = (struct ext2_dir_entry *) (block_start + offset);
		if (offset == 0) {
			if (ent->hash == prev_hash)
				outdir->hashes[outdir->num-1] = ent->hash | 1;
			else
				outdir->hashes[outdir->num-1] = ent->hash;
		}
		dirent->inode = ent->dir->inode;
		dirent->name_len = ent->dir->name_len;
		dirent->rec_len = rec_len;
		memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
		offset += rec_len;
		left -= rec_len;
		if (left < 12) {
			dirent->rec_len += left;
			offset += left;
			left = 0;
		}
		prev_hash = ent->hash;
	}
	if (left)
		dirent->rec_len += left;

	return 0;
}
Esempio n. 17
0
static int fill_dir_block(ext2_filsys fs,
			  blk_t	*block_nr,
			  e2_blkcnt_t blockcnt,
			  blk_t ref_block EXT2FS_ATTR((unused)),
			  int ref_offset EXT2FS_ATTR((unused)),
			  void *priv_data)
{
	struct fill_dir_struct	*fd = (struct fill_dir_struct *) priv_data;
	struct hash_entry 	*new_array, *ent;
	struct ext2_dir_entry 	*dirent;
	char			*dir;
	unsigned int		offset, dir_offset;
	
	if (blockcnt < 0)
		return 0;

	offset = blockcnt * fs->blocksize;
	if (offset + fs->blocksize > fd->inode->i_size) {
		fd->err = EXT2_ET_DIR_CORRUPTED;
		return BLOCK_ABORT;
	}
	dir = (fd->buf+offset);
	if (HOLE_BLKADDR(*block_nr)) {
		memset(dir, 0, fs->blocksize);
		dirent = (struct ext2_dir_entry *) dir;
		dirent->rec_len = fs->blocksize;
	} else {
		fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
		if (fd->err)
			return BLOCK_ABORT;
	}
	/* While the directory block is "hot", index it. */
	dir_offset = 0;
	while (dir_offset < fs->blocksize) {
		dirent = (struct ext2_dir_entry *) (dir + dir_offset);
		if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
		    (dirent->rec_len < 8) ||
		    ((dirent->rec_len % 4) != 0) ||
		    (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
			fd->err = EXT2_ET_DIR_CORRUPTED;
			return BLOCK_ABORT;
		}
		dir_offset += dirent->rec_len;
		if (dirent->inode == 0)
			continue;
		if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
		    (dirent->name[0] == '.'))
			continue;
		if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
		    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
			fd->parent = dirent->inode;
			continue;
		}
		if (fd->num_array >= fd->max_array) {
			new_array = realloc(fd->harray,
			    sizeof(struct hash_entry) * (fd->max_array+500));
			if (!new_array) {
				fd->err = ENOMEM;
				return BLOCK_ABORT;
			}
			fd->harray = new_array;
			fd->max_array += 500;
		}
		ent = fd->harray + fd->num_array++;
		ent->dir = dirent;
		fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
		if (fd->compress)
			ent->hash = ent->minor_hash = 0;
		else {
			fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
						 dirent->name,
						 dirent->name_len & 0xFF,
						 fs->super->s_hash_seed,
						 &ent->hash, &ent->minor_hash);
			if (fd->err)
				return BLOCK_ABORT;
		}
	}
	
	return 0;
}
Esempio n. 18
0
static int ext2_readdir (struct inode * inode, struct file * filp,
			 struct dirent * dirent, int count)
{
	unsigned long offset, blk;
	int i, num, stored, dlen;
	struct buffer_head * bh, * tmp, * bha[16];
	struct ext2_dir_entry * de;
	struct super_block * sb;
	int err, version;

	if (!inode || !S_ISDIR(inode->i_mode))
		return -EBADF;
	sb = inode->i_sb;

	stored = 0;
	bh = NULL;
	offset = filp->f_pos & (sb->s_blocksize - 1);

	while (count > 0 && !stored && filp->f_pos < inode->i_size) {
		blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
		bh = ext2_bread (inode, blk, 0, &err);
		if (!bh) {
			filp->f_pos += sb->s_blocksize - offset;
			continue;
		}

		/*
		 * Do the readahead
		 */
		if (!offset) {
			for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
			     i > 0; i--) {
				tmp = ext2_getblk (inode, ++blk, 0, &err);
				if (tmp && !tmp->b_uptodate && !tmp->b_lock)
					bha[num++] = tmp;
				else
					brelse (tmp);
			}
			if (num) {
				ll_rw_block (READA, num, bha);
				for (i = 0; i < num; i++)
					brelse (bha[i]);
			}
		}
		
revalidate:
		/* If the dir block has changed since the last call to
		 * readdir(2), then we might be pointing to an invalid
		 * dirent right now.  Scan from the start of the block
		 * to make sure. */
		if (filp->f_version != inode->i_version) {
			for (i = 0; i < sb->s_blocksize && i < offset; ) {
				de = (struct ext2_dir_entry *) 
					(bh->b_data + i);
				/* It's too expensive to do a full
				 * dirent test each time round this
				 * loop, but we do have to test at
				 * least that it is non-zero.  A
				 * failure will be detected in the
				 * dirent test below. */
				if (de->rec_len < EXT2_DIR_REC_LEN(1))
					break;
				i += de->rec_len;
			}
			offset = i;
			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
				| offset;
			filp->f_version = inode->i_version;
		}
		
		while (count > 0 && filp->f_pos < inode->i_size 
		       && offset < sb->s_blocksize) {
			de = (struct ext2_dir_entry *) (bh->b_data + offset);
			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
						   bh, offset)) {
				/* On error, skip the f_pos to the
                                   next block. */
				filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
					      + sb->s_blocksize;
				brelse (bh);
				return stored;
			}
			if (de->inode) {
				dlen = ROUND_UP(NAME_OFFSET(dirent) 
						+ de->name_len + 1);
				/* Old libc libraries always use a
                                   count of 1. */
				if (count == 1 && !stored)
					count = dlen;
				if (count < dlen) {
					count = 0;
					break;
				}

				/* We might block in the next section
				 * if the data destination is
				 * currently swapped out.  So, use a
				 * version stamp to detect whether or
				 * not the directory has been modified
				 * during the copy operation. */
				version = inode->i_version;
				i = de->name_len;
				memcpy_tofs (dirent->d_name, de->name, i);
				put_fs_long (de->inode, &dirent->d_ino);
				put_fs_byte (0, dirent->d_name + i);
				put_fs_word (i, &dirent->d_reclen);
				put_fs_long (dlen, &dirent->d_off);
				if (version != inode->i_version)
					goto revalidate;
				dcache_add(inode, de->name, de->name_len,
						 de->inode);

				stored += dlen;
				count -= dlen;
				((char *) dirent) += dlen;
			}
			offset += de->rec_len;
			filp->f_pos += de->rec_len;
		}
		offset = 0;
		brelse (bh);
	}
	if (!IS_RDONLY(inode)) {
		inode->i_atime = CURRENT_TIME;
		inode->i_dirt = 1;
	}
	return stored;
}
Esempio n. 19
0
File: link.c Progetto: haie1011/nofs
static int link_proc(struct ext2_dir_entry *dirent,
		     int	offset,
		     int	blocksize,
		     char	*buf,
		     void	*priv_data)
{

	struct link_struct *ls = (struct link_struct *) priv_data;
	struct ext2_dir_entry *next;
	unsigned int rec_len, min_rec_len, curr_rec_len;
	int ret = 0;

	printf("Trying to link inode %lu at offset %lu\n", ls->inode, offset);
	rec_len = EXT2_DIR_REC_LEN(ls->namelen);

	ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
	if (ls->err) {
		printf("Error: Problem with reading current rec len\n");
		return DIRENT_ABORT;
	}

	printf("Current rec len: %d\n", curr_rec_len);

	/*
	 * See if the following directory entry (if any) is unused;
	 * if so, absorb it into this one.
	 */
	next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
	if ((offset + curr_rec_len < blocksize - 8) &&
	    (next->inode == 0) &&
	    (offset + curr_rec_len + next->rec_len <= blocksize)) {
		printf("Absorbing following dirent into this one\n");
		curr_rec_len += next->rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err) {
			printf("Problem with setting rec len for new dirent\n");
			return DIRENT_ABORT;
		}
		ret = DIRENT_CHANGED;
	}

	/*
	 * If the directory entry is used, see if we can split the
	 * directory entry to make room for the new name.  If so,
	 * truncate it and return.
	 */
	if (dirent->inode) {
		printf("Handling used dirent\n");
		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
		if (curr_rec_len < (min_rec_len + rec_len))
			return ret;
		rec_len = curr_rec_len - min_rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		next = (struct ext2_dir_entry *) (buf + offset +
						  dirent->rec_len);
		next->inode = 0;
		next->name_len = 0;
		ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
		if (ls->err)
			return DIRENT_ABORT;
		return DIRENT_CHANGED;
	}

	/*
	 * If we get this far, then the directory entry is not used.
	 * See if we can fit the request entry in.  If so, do it.
	 */
	if (curr_rec_len < rec_len)
		return ret;
	printf("Setting dirent based on ls information\n");
	dirent->inode = ls->inode;
	dirent->name_len = ls->namelen;
	strncpy(dirent->name, ls->name, ls->namelen);
	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
		dirent->name_len |= (ls->flags & 0x7) << 8;

	ls->done++;
	return DIRENT_ABORT|DIRENT_CHANGED;
}
Esempio n. 20
0
bool
ext2_add_entry( PEXT2_FILESYS Ext2Sys,
                ULONG parent, ULONG inode,
                int filetype, char *name )
{
    PEXT2_DIR_ENTRY2  dir = NULL, newdir = NULL;
    EXT2_INODE      parent_inode;
    ULONG       dwRet;
    char        *buf;
    int         rec_len;
    bool        bRet = false;

    rec_len = EXT2_DIR_REC_LEN(strlen(name));

    if (!ext2_load_inode(Ext2Sys, parent, &parent_inode))
    {
        return false;
    }

    buf = (char *)RtlAllocateHeap(RtlGetProcessHeap(), 0, parent_inode.i_size);

    if (!ext2_read_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet))
    {
        return false;
    }

    dir = (PEXT2_DIR_ENTRY2) buf;

    while ((char *)dir < buf + parent_inode.i_size)
    {
        if ((dir->inode == 0 && dir->rec_len >= rec_len) || 
               (dir->rec_len >= dir->name_len + rec_len) )
        {
            if (dir->inode)
            {
                newdir = (PEXT2_DIR_ENTRY2) ((PUCHAR)dir + EXT2_DIR_REC_LEN(dir->name_len));
                newdir->rec_len = dir->rec_len - EXT2_DIR_REC_LEN(dir->name_len);

                dir->rec_len = EXT2_DIR_REC_LEN(dir->name_len);

                dir = newdir;
            }

            dir->file_type = filetype;
            dir->inode = inode;
            dir->name_len = strlen(name);
            memcpy(dir->name, name, strlen(name));
    
            bRet = true;
            break;
        }

        dir = (PEXT2_DIR_ENTRY2) (dir->rec_len + (PUCHAR) dir);
    }


    if (bRet)
        return ext2_write_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet);

    return bRet;
}
Esempio n. 21
0
static int check_dir_block(ext2_filsys fs,
			   struct ext2_db_entry2 *db,
			   void *priv_data)
{
 	struct dx_dir_info	*dx_dir;
#ifdef ENABLE_HTREE
	struct dx_dirblock_info	*dx_db = 0;
#endif /* ENABLE_HTREE */
	struct ext2_dir_entry 	*dirent, *prev, dot, dotdot;
	ext2_dirhash_t		hash;
	unsigned int		offset = 0;
	int			dir_modified = 0;
	int			dot_state;
	unsigned int		rec_len;
	blk64_t			block_nr = db->blk;
	ext2_ino_t 		ino = db->ino;
	ext2_ino_t 		subdir_parent;
	__u16			links;
	struct check_dir_struct	*cd;
	char 			*buf;
	e2fsck_t		ctx;
	problem_t		problem;
	struct ext2_dx_root_info *root;
	struct ext2_dx_countlimit *limit;
	static dict_t de_dict;
	struct problem_context	pctx;
	int	dups_found = 0;
	int	ret;
	int	dx_csum_size = 0, de_csum_size = 0;
	int	failed_csum = 0;
	int	is_leaf = 1;
	size_t	inline_data_size = 0;
	int	filetype = 0;

	cd = (struct check_dir_struct *) priv_data;
	buf = cd->buf;
	ctx = cd->ctx;

	if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
		return DIRENT_ABORT;

	if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
		return DIRENT_ABORT;

	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
		dx_csum_size = sizeof(struct ext2_dx_tail);
		de_csum_size = sizeof(struct ext2_dir_entry_tail);
	}

	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
				      EXT2_FEATURE_INCOMPAT_FILETYPE))
		filetype = EXT2_FT_DIR << 8;

	/*
	 * Make sure the inode is still in use (could have been
	 * deleted in the duplicate/bad blocks pass.
	 */
	if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino)))
		return 0;

	cd->pctx.ino = ino;
	cd->pctx.blk = block_nr;
	cd->pctx.blkcount = db->blockcnt;
	cd->pctx.ino2 = 0;
	cd->pctx.dirent = 0;
	cd->pctx.num = 0;

	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
				      EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
		errcode_t ec;

		ec = ext2fs_inline_data_size(fs, ino, &inline_data_size);
		if (ec && ec != EXT2_ET_NO_INLINE_DATA)
			return DIRENT_ABORT;
	}

	if (db->blk == 0 && !inline_data_size) {
		if (allocate_dir_block(ctx, db, buf, &cd->pctx))
			return 0;
		block_nr = db->blk;
	}

	if (db->blockcnt)
		dot_state = 2;
	else
		dot_state = 0;

	if (ctx->dirs_to_hash &&
	    ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
		dups_found++;

#if 0
	printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
	       db->blockcnt, ino);
#endif

	ehandler_operation(_("reading directory block"));
	if (inline_data_size)
		cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0);
	else
		cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr,
							  buf, 0, ino);
	ehandler_operation(0);
	if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
		cd->pctx.errcode = 0; /* We'll handle this ourselves */
	else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) {
		cd->pctx.errcode = 0; /* We'll handle this ourselves */
		failed_csum = 1;
	}
	if (cd->pctx.errcode) {
		char *buf2;
		if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
			ctx->flags |= E2F_FLAG_ABORT;
			return DIRENT_ABORT;
		}
		ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0,
				     EXT2_ROOT_INO, &buf2);
		memcpy(buf, buf2, fs->blocksize);
		ext2fs_free_mem(&buf2);
	}
#ifdef ENABLE_HTREE
	dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
	if (dx_dir && dx_dir->numblocks) {
		if (db->blockcnt >= dx_dir->numblocks) {
			if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK,
					&pctx)) {
				clear_htree(ctx, ino);
				dx_dir->numblocks = 0;
				dx_db = 0;
				goto out_htree;
			}
			fatal_error(ctx, _("Can not continue."));
		}
		dx_db = &dx_dir->dx_block[db->blockcnt];
		dx_db->type = DX_DIRBLOCK_LEAF;
		dx_db->phys = block_nr;
		dx_db->min_hash = ~0;
		dx_db->max_hash = 0;

		dirent = (struct ext2_dir_entry *) buf;
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		limit = (struct ext2_dx_countlimit *) (buf+8);
		if (db->blockcnt == 0) {
			root = (struct ext2_dx_root_info *) (buf + 24);
			dx_db->type = DX_DIRBLOCK_ROOT;
			dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
			if ((root->reserved_zero ||
			     root->info_length < 8 ||
			     root->indirect_levels > 1) &&
			    fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
				clear_htree(ctx, ino);
				dx_dir->numblocks = 0;
				dx_db = 0;
			}
			dx_dir->hashversion = root->hash_version;
			if ((dx_dir->hashversion <= EXT2_HASH_TEA) &&
			    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
				dx_dir->hashversion += 3;
			dx_dir->depth = root->indirect_levels + 1;
		} else if ((dirent->inode == 0) &&
			   (rec_len == fs->blocksize) &&
			   (ext2fs_dirent_name_len(dirent) == 0) &&
			   (ext2fs_le16_to_cpu(limit->limit) ==
			    ((fs->blocksize - (8 + dx_csum_size)) /
			     sizeof(struct ext2_dx_entry))))
			dx_db->type = DX_DIRBLOCK_NODE;
		is_leaf = 0;
	}
out_htree:
#endif /* ENABLE_HTREE */

	/* Verify checksum. */
	if (is_leaf && de_csum_size && !inline_data_size) {
		/* No space for csum?  Rebuild dirs in pass 3A. */
		if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
			de_csum_size = 0;
			if (e2fsck_dir_will_be_rehashed(ctx, ino))
				goto skip_checksum;
			if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
					 &cd->pctx))
				goto skip_checksum;
			e2fsck_rehash_dir_later(ctx, ino);
			goto skip_checksum;
		}
		if (failed_csum) {
			char *buf2;
			if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_CSUM_INVALID,
					 &cd->pctx))
				goto skip_checksum;
			ext2fs_new_dir_block(fs,
					     db->blockcnt == 0 ? ino : 0,
					     EXT2_ROOT_INO, &buf2);
			memcpy(buf, buf2, fs->blocksize);
			ext2fs_free_mem(&buf2);
			dir_modified++;
			failed_csum = 0;
		}
	}
	/* htree nodes don't use fake dirents to store checksums */
	if (!is_leaf)
		de_csum_size = 0;

skip_checksum:
	dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
	prev = 0;
	do {
		dgrp_t group;
		ext2_ino_t first_unused_inode;
		unsigned int name_len;

		problem = 0;
		if (!inline_data_size || dot_state > 1) {
			dirent = (struct ext2_dir_entry *) (buf + offset);
			(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
			cd->pctx.dirent = dirent;
			cd->pctx.num = offset;
			if (((offset + rec_len) > fs->blocksize) ||
			    (rec_len < 12) ||
			    ((rec_len % 4) != 0) ||
			    ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
				if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
					salvage_directory(fs, dirent, prev, &offset);
					dir_modified++;
					continue;
				} else
					goto abort_free_dict;
			}
		} else {
			if (dot_state == 0) {
				memset(&dot, 0, sizeof(dot));
				dirent = &dot;
				dirent->inode = ino;
				dirent->rec_len = EXT2_DIR_REC_LEN(1);
				dirent->name_len = 1 | filetype;
				dirent->name[0] = '.';
			} else if (dot_state == 1) {
				memset(&dotdot, 0, sizeof(dotdot));
				dirent = &dotdot;
				dirent->inode =
					((struct ext2_dir_entry *)buf)->inode;
				dirent->rec_len = EXT2_DIR_REC_LEN(2);
				dirent->name_len = 2 | filetype;
				dirent->name[0] = '.';
				dirent->name[1] = '.';
			} else {
				fatal_error(ctx, _("Can not continue."));
			}
			cd->pctx.dirent = dirent;
			cd->pctx.num = offset;
		}

		if (dot_state == 0) {
			if (check_dot(ctx, dirent, ino, &cd->pctx))
				dir_modified++;
		} else if (dot_state == 1) {
			ret = check_dotdot(ctx, dirent, ino, &cd->pctx);
			if (ret < 0)
				goto abort_free_dict;
			if (ret)
				dir_modified++;
		} else if (dirent->inode == ino) {
			problem = PR_2_LINK_DOT;
			if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			}
		}
		if (!dirent->inode)
			goto next;

		/*
		 * Make sure the inode listed is a legal one.
		 */
		name_len = ext2fs_dirent_name_len(dirent);
		if (((dirent->inode != EXT2_ROOT_INO) &&
		     (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
		    (dirent->inode > fs->super->s_inodes_count)) {
			problem = PR_2_BAD_INO;
		} else if (ctx->inode_bb_map &&
			   (ext2fs_test_inode_bitmap2(ctx->inode_bb_map,
						     dirent->inode))) {
			/*
			 * If the inode is in a bad block, offer to
			 * clear it.
			 */
			problem = PR_2_BB_INODE;
		} else if ((dot_state > 1) && (name_len == 1) &&
			   (dirent->name[0] == '.')) {
			/*
			 * If there's a '.' entry in anything other
			 * than the first directory entry, it's a
			 * duplicate entry that should be removed.
			 */
			problem = PR_2_DUP_DOT;
		} else if ((dot_state > 1) && (name_len == 2) &&
			   (dirent->name[0] == '.') &&
			   (dirent->name[1] == '.')) {
			/*
			 * If there's a '..' entry in anything other
			 * than the second directory entry, it's a
			 * duplicate entry that should be removed.
			 */
			problem = PR_2_DUP_DOT_DOT;
		} else if ((dot_state > 1) &&
			   (dirent->inode == EXT2_ROOT_INO)) {
			/*
			 * Don't allow links to the root directory.
			 * We check this specially to make sure we
			 * catch this error case even if the root
			 * directory hasn't been created yet.
			 */
			problem = PR_2_LINK_ROOT;
		} else if ((dot_state > 1) && (name_len == 0)) {
			/*
			 * Don't allow zero-length directory names.
			 */
			problem = PR_2_NULL_NAME;
		}

		if (problem) {
			if (fix_problem(ctx, problem, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		/*
		 * If the inode was marked as having bad fields in
		 * pass1, process it and offer to fix/clear it.
		 * (We wait until now so that we can display the
		 * pathname to the user.)
		 */
		if (ctx->inode_bad_map &&
		    ext2fs_test_inode_bitmap2(ctx->inode_bad_map,
					     dirent->inode)) {
			if (e2fsck_process_bad_inode(ctx, ino,
						     dirent->inode,
						     buf + fs->blocksize)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			}
			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
				return DIRENT_ABORT;
		}

		group = ext2fs_group_of_ino(fs, dirent->inode);
		first_unused_inode = group * fs->super->s_inodes_per_group +
					1 + fs->super->s_inodes_per_group -
					ext2fs_bg_itable_unused(fs, group);
		cd->pctx.group = group;

		/*
		 * Check if the inode was missed out because
		 * _INODE_UNINIT flag was set or bg_itable_unused was
		 * incorrect.  If so, clear the _INODE_UNINIT flag and
		 * restart e2fsck.  In the future it would be nice if
		 * we could call a function in pass1.c that checks the
		 * newly visible inodes.
		 */
		if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) {
			pctx.num = dirent->inode;
			if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT,
					&cd->pctx)){
				ext2fs_bg_flags_clear(fs, group,
						      EXT2_BG_INODE_UNINIT);
				ext2fs_mark_super_dirty(fs);
				ctx->flags |= E2F_FLAG_RESTART_LATER;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		} else if (dirent->inode >= first_unused_inode) {
			pctx.num = dirent->inode;
			if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){
				ext2fs_bg_itable_unused_set(fs, group, 0);
				ext2fs_mark_super_dirty(fs);
				ctx->flags |= E2F_FLAG_RESTART_LATER;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		/* 
		 * Offer to clear unused inodes; if we are going to be
		 * restarting the scan due to bg_itable_unused being
		 * wrong, then don't clear any inodes to avoid zapping
		 * inodes that were skipped during pass1 due to an
		 * incorrect bg_itable_unused; we'll get any real
		 * problems after we restart.
		 */
		if (!(ctx->flags & E2F_FLAG_RESTART_LATER) &&
		    !(ext2fs_test_inode_bitmap2(ctx->inode_used_map,
						dirent->inode)))
			problem = PR_2_UNUSED_INODE;

		if (problem) {
			if (fix_problem(ctx, problem, &cd->pctx)) {
				dirent->inode = 0;
				dir_modified++;
				goto next;
			} else {
				ext2fs_unmark_valid(fs);
				if (problem == PR_2_BAD_INO)
					goto next;
			}
		}

		if (check_name(ctx, dirent, ino, &cd->pctx))
			dir_modified++;

		if (check_filetype(ctx, dirent, ino, &cd->pctx))
			dir_modified++;

#ifdef ENABLE_HTREE
		if (dx_db) {
			ext2fs_dirhash(dx_dir->hashversion, dirent->name,
				       ext2fs_dirent_name_len(dirent),
				       fs->super->s_hash_seed, &hash, 0);
			if (hash < dx_db->min_hash)
				dx_db->min_hash = hash;
			if (hash > dx_db->max_hash)
				dx_db->max_hash = hash;
		}
#endif

		/*
		 * If this is a directory, then mark its parent in its
		 * dir_info structure.  If the parent field is already
		 * filled in, then this directory has more than one
		 * hard link.  We assume the first link is correct,
		 * and ask the user if he/she wants to clear this one.
		 */
		if ((dot_state > 1) &&
		    (ext2fs_test_inode_bitmap2(ctx->inode_dir_map,
					      dirent->inode))) {
			if (e2fsck_dir_info_get_parent(ctx, dirent->inode,
						       &subdir_parent)) {
				cd->pctx.ino = dirent->inode;
				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
				goto abort_free_dict;
			}
			if (subdir_parent) {
				cd->pctx.ino2 = subdir_parent;
				if (fix_problem(ctx, PR_2_LINK_DIR,
						&cd->pctx)) {
					dirent->inode = 0;
					dir_modified++;
					goto next;
				}
				cd->pctx.ino2 = 0;
			} else {
				(void) e2fsck_dir_info_set_parent(ctx,
						  dirent->inode, ino);
			}
		}

		if (dups_found) {
			;
		} else if (dict_lookup(&de_dict, dirent)) {
			clear_problem_context(&pctx);
			pctx.ino = ino;
			pctx.dirent = dirent;
			fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
			e2fsck_rehash_dir_later(ctx, ino);
			dups_found++;
		} else
			dict_alloc_insert(&de_dict, dirent, dirent);

		ext2fs_icount_increment(ctx->inode_count, dirent->inode,
					&links);
		if (links > 1)
			ctx->fs_links_count++;
		ctx->fs_total_count++;
	next:
		prev = dirent;
		if (dir_modified)
			(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		if (!inline_data_size || dot_state > 1) {
			offset += rec_len;
		} else {
			if (dot_state == 1)
				offset = 4;
		}
		dot_state++;
	} while (is_last_entry(fs, inline_data_size, offset, de_csum_size));
#if 0
	printf("\n");
#endif
#ifdef ENABLE_HTREE
	if (dx_db) {
#ifdef DX_DEBUG
		printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
		       db->blockcnt, dx_db->type,
		       dx_db->min_hash, dx_db->max_hash);
#endif
		cd->pctx.dir = cd->pctx.ino;
		if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
		    (dx_db->type == DX_DIRBLOCK_NODE))
			parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
	}
#endif /* ENABLE_HTREE */

	if (inline_data_size) {
		if (offset != inline_data_size) {
			cd->pctx.num = rec_len + offset - inline_data_size;
			if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
				dirent->rec_len = cd->pctx.num;
				dir_modified++;
			}
		}
	} else {
		if (offset != fs->blocksize - de_csum_size) {
			cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
				       offset;
			if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
				dirent->rec_len = cd->pctx.num;
				dir_modified++;
			}
		}
	}
	if (dir_modified) {
		/* leaf block with no tail?  Rehash dirs later. */
		if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
		    is_leaf &&
		    !ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf))
			e2fsck_rehash_dir_later(ctx, ino);

write_and_fix:
		if (e2fsck_dir_will_be_rehashed(ctx, ino))
			ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
		if (inline_data_size) {
			cd->pctx.errcode =
				ext2fs_inline_data_set(fs, ino, 0, buf,
						       inline_data_size);
		} else
			cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr,
								   buf, 0, ino);
		if (e2fsck_dir_will_be_rehashed(ctx, ino))
			ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
		if (cd->pctx.errcode) {
			if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
					 &cd->pctx))
				goto abort_free_dict;
		}
		ext2fs_mark_changed(fs);
	} else if (is_leaf && failed_csum && !dir_modified) {
		/*
		 * If a leaf node that fails csum makes it this far without
		 * alteration, ask the user if the checksum should be fixed.
		 */
		if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
				&cd->pctx))
			goto write_and_fix;
	}
	dict_free_nodes(&de_dict);
	return 0;
abort_free_dict:
	ctx->flags |= E2F_FLAG_ABORT;
	dict_free_nodes(&de_dict);
	return DIRENT_ABORT;
}
Esempio n. 22
0
/*
 * Write a directory entry after a call to namei, using the parameters
 * that it left in the directory inode.  The argument ip is the inode which
 * the new directory entry will refer to.  Dvp is a pointer to the directory
 * to be written, which was left locked by namei. Remaining parameters
 * (dp->i_offset, dp->i_count) indicate how the space for the new
 * entry is to be obtained.
 */
int
ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp)
{
	struct ext2_dir_entry_2 *ep, *nep;
	struct inode *dp;
	struct buf *bp;
	struct ext2_dir_entry_2 newdir;
	struct iovec aiov;
	struct uio auio;
	u_int dsize;
	int error, loc, newentrysize, spacefree;
	char *dirbuf;
	int     DIRBLKSIZ = ip->i_e2fs->s_blocksize;


	dp = VTOI(dvp);
	newdir.inode = ip->i_number;
	newdir.name_len = cnp->cn_namelen;
	if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
	    EXT2_FEATURE_INCOMPAT_FILETYPE))
		newdir.file_type = DTTOFT(IFTODT(ip->i_mode));
	else
		newdir.file_type = EXT2_FT_UNKNOWN;
	bcopy(cnp->cn_nameptr, newdir.name, (unsigned)cnp->cn_namelen + 1);
	newentrysize = EXT2_DIR_REC_LEN(newdir.name_len);
	if (dp->i_count == 0) {
		/*
		 * If dp->i_count is 0, then namei could find no
		 * space in the directory. Here, dp->i_offset will
		 * be on a directory block boundary and we will write the
		 * new entry into a fresh block.
		 */
		if (dp->i_offset & (DIRBLKSIZ - 1))
			panic("ext2_direnter: newblk");
		auio.uio_offset = dp->i_offset;
		newdir.rec_len = DIRBLKSIZ;
		auio.uio_resid = newentrysize;
		aiov.iov_len = newentrysize;
		aiov.iov_base = (caddr_t)&newdir;
		auio.uio_iov = &aiov;
		auio.uio_iovcnt = 1;
		auio.uio_rw = UIO_WRITE;
		auio.uio_segflg = UIO_SYSSPACE;
		auio.uio_td = NULL;
		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
		if (DIRBLKSIZ >
		    VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
			/* XXX should grow with balloc() */
			panic("ext2_direnter: frag size");
		else if (!error) {
			dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
			dp->i_flag |= IN_CHANGE;
		}
		return (error);
	}

	/*
	 * If dp->i_count is non-zero, then namei found space
	 * for the new entry in the range dp->i_offset to
	 * dp->i_offset + dp->i_count in the directory.
	 * To use this space, we may have to compact the entries located
	 * there, by copying them together towards the beginning of the
	 * block, leaving the free space in one usable chunk at the end.
	 */

	/*
	 * Increase size of directory if entry eats into new space.
	 * This should never push the size past a new multiple of
	 * DIRBLKSIZE.
	 *
	 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
	 */
	if (dp->i_offset + dp->i_count > dp->i_size)
		dp->i_size = dp->i_offset + dp->i_count;
	/*
	 * Get the block containing the space for the new directory entry.
	 */
	if ((error = EXT2_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0)
		return (error);
	/*
	 * Find space for the new entry. In the simple case, the entry at
	 * offset base will have the space. If it does not, then namei
	 * arranged that compacting the region dp->i_offset to
	 * dp->i_offset + dp->i_count would yield the
	 * space.
	 */
	ep = (struct ext2_dir_entry_2 *)dirbuf;
	dsize = EXT2_DIR_REC_LEN(ep->name_len);
	spacefree = ep->rec_len - dsize;
	for (loc = ep->rec_len; loc < dp->i_count; ) {
		nep = (struct ext2_dir_entry_2 *)(dirbuf + loc);
		if (ep->inode) {
			/* trim the existing slot */
			ep->rec_len = dsize;
			ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
		} else {
			/* overwrite; nothing there; header is ours */
			spacefree += dsize;
		}
		dsize = EXT2_DIR_REC_LEN(nep->name_len);
		spacefree += nep->rec_len - dsize;
		loc += nep->rec_len;
		bcopy((caddr_t)nep, (caddr_t)ep, dsize);
	}
	/*
	 * Update the pointer fields in the previous entry (if any),
	 * copy in the new entry, and write out the block.
	 */
	if (ep->inode == 0) {
		if (spacefree + dsize < newentrysize)
			panic("ext2_direnter: compact1");
		newdir.rec_len = spacefree + dsize;
	} else {
		if (spacefree < newentrysize)
			panic("ext2_direnter: compact2");
		newdir.rec_len = spacefree;
		ep->rec_len = dsize;
		ep = (struct ext2_dir_entry_2 *)((char *)ep + dsize);
	}
	bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
	error = bwrite(bp);
	dp->i_flag |= IN_CHANGE | IN_UPDATE;
	if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
		error = EXT2_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC,
				      cnp->cn_cred);
	return (error);
}
Esempio n. 23
0
static int fill_dir_block(ext2_filsys fs,
			  blk64_t *block_nr,
			  e2_blkcnt_t blockcnt,
			  blk64_t ref_block EXT2FS_ATTR((unused)),
			  int ref_offset EXT2FS_ATTR((unused)),
			  void *priv_data)
{
	struct fill_dir_struct	*fd = (struct fill_dir_struct *) priv_data;
	struct hash_entry 	*new_array, *ent;
	struct ext2_dir_entry 	*dirent;
	char			*dir;
	unsigned int		offset, dir_offset, rec_len, name_len;
	int			hash_alg;

	if (blockcnt < 0)
		return 0;

	offset = blockcnt * fs->blocksize;
	if (offset + fs->blocksize > fd->inode->i_size) {
		fd->err = EXT2_ET_DIR_CORRUPTED;
		return BLOCK_ABORT;
	}

	dir = (fd->buf+offset);
	if (*block_nr == 0) {
		memset(dir, 0, fs->blocksize);
		dirent = (struct ext2_dir_entry *) dir;
		(void) ext2fs_set_rec_len(fs, fs->blocksize, dirent);
	} else {
		int flags = fs->flags;
		fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
		fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0,
						 fd->dir);
		fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
			    (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
		if (fd->err)
			return BLOCK_ABORT;
	}
	hash_alg = fs->super->s_def_hash_version;
	if ((hash_alg <= EXT2_HASH_TEA) &&
	    (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
		hash_alg += 3;
	/* While the directory block is "hot", index it. */
	dir_offset = 0;
	while (dir_offset < fs->blocksize) {
		dirent = (struct ext2_dir_entry *) (dir + dir_offset);
		(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
		name_len = ext2fs_dirent_name_len(dirent);
		if (((dir_offset + rec_len) > fs->blocksize) ||
		    (rec_len < 8) ||
		    ((rec_len % 4) != 0) ||
		    (name_len + 8 > rec_len)) {
			fd->err = EXT2_ET_DIR_CORRUPTED;
			return BLOCK_ABORT;
		}
		dir_offset += rec_len;
		if (dirent->inode == 0)
			continue;
		if (!fd->compress && (name_len == 1) &&
		    (dirent->name[0] == '.'))
			continue;
		if (!fd->compress && (name_len == 2) &&
		    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
			fd->parent = dirent->inode;
			continue;
		}
		if (fd->num_array >= fd->max_array) {
			new_array = realloc(fd->harray,
			    sizeof(struct hash_entry) * (fd->max_array+500));
			if (!new_array) {
				fd->err = ENOMEM;
				return BLOCK_ABORT;
			}
			fd->harray = new_array;
			fd->max_array += 500;
		}
		ent = fd->harray + fd->num_array++;
		ent->dir = dirent;
		fd->dir_size += EXT2_DIR_REC_LEN(name_len);
		ent->ino = dirent->inode;
		if (fd->compress)
			ent->hash = ent->minor_hash = 0;
		else {
			fd->err = ext2fs_dirhash(hash_alg, dirent->name,
						 name_len,
						 fs->super->s_hash_seed,
						 &ent->hash, &ent->minor_hash);
			if (fd->err)
				return BLOCK_ABORT;
		}
	}

	return 0;
}
Esempio n. 24
0
static int link_proc(struct ext2_dir_entry *dirent,
		     int	offset,
		     int	blocksize,
		     char	*buf,
		     void	*priv_data)
{
	struct link_struct *ls = (struct link_struct *) priv_data;
	struct ext2_dir_entry *next;
	unsigned int rec_len, min_rec_len, curr_rec_len;
	int ret = 0;
	int csum_size = 0;
	struct ext2_dir_entry_tail *t;

	if (ls->done)
		return DIRENT_ABORT;

	rec_len = EXT2_DIR_REC_LEN(ls->namelen);

	ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
	if (ls->err)
		return DIRENT_ABORT;

	if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
		csum_size = sizeof(struct ext2_dir_entry_tail);
	/*
	 * See if the following directory entry (if any) is unused;
	 * if so, absorb it into this one.
	 */
	next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
	if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
	    (next->inode == 0) &&
	    (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
		curr_rec_len += next->rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		ret = DIRENT_CHANGED;
	}

	/*
	 * Since ext2fs_link blows away htree data, we need to be
	 * careful -- if metadata_csum is enabled and we're passed in
	 * a dirent that contains htree data, we need to create the
	 * fake entry at the end of the block that hides the checksum.
	 */

	/* De-convert a dx_node block */
	if (csum_size &&
	    curr_rec_len == ls->fs->blocksize &&
	    !dirent->inode) {
		curr_rec_len -= csum_size;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
		ext2fs_initialize_dirent_tail(ls->fs, t);
		ret = DIRENT_CHANGED;
	}

	/* De-convert a dx_root block */
	if (csum_size &&
	    curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) &&
	    offset == EXT2_DIR_REC_LEN(1) &&
	    dirent->name[0] == '.' && dirent->name[1] == '.') {
		curr_rec_len -= csum_size;
		ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
		ext2fs_initialize_dirent_tail(ls->fs, t);
		ret = DIRENT_CHANGED;
	}

	/*
	 * If the directory entry is used, see if we can split the
	 * directory entry to make room for the new name.  If so,
	 * truncate it and return.
	 */
	if (dirent->inode) {
		min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent));
		if (curr_rec_len < (min_rec_len + rec_len))
			return ret;
		rec_len = curr_rec_len - min_rec_len;
		ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
		if (ls->err)
			return DIRENT_ABORT;
		next = (struct ext2_dir_entry *) (buf + offset +
						  dirent->rec_len);
		next->inode = 0;
		ext2fs_dirent_set_name_len(next, 0);
		ext2fs_dirent_set_file_type(next, 0);
		ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
		if (ls->err)
			return DIRENT_ABORT;
		return DIRENT_CHANGED;
	}

	/*
	 * If we get this far, then the directory entry is not used.
	 * See if we can fit the request entry in.  If so, do it.
	 */
	if (curr_rec_len < rec_len)
		return ret;
	dirent->inode = ls->inode;
	ext2fs_dirent_set_name_len(dirent, ls->namelen);
	strncpy(dirent->name, ls->name, ls->namelen);
	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
		ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7);

	ls->done++;
	return DIRENT_ABORT|DIRENT_CHANGED;
}
Esempio n. 25
0
static errcode_t copy_dir_entries(e2fsck_t ctx,
				  struct fill_dir_struct *fd,
				  struct out_dir *outdir)
{
	ext2_filsys 		fs = ctx->fs;
	errcode_t		retval;
	char			*block_start;
	struct hash_entry 	*ent;
	struct ext2_dir_entry	*dirent;
	unsigned int		rec_len, prev_rec_len, left, slack, offset;
	int			i;
	ext2_dirhash_t		prev_hash;
	int			csum_size = 0;
	struct			ext2_dir_entry_tail *t;

	if (ctx->htree_slack_percentage == 255) {
		profile_get_uint(ctx->profile, "options",
				 "indexed_dir_slack_percentage",
				 0, 20,
				 &ctx->htree_slack_percentage);
		if (ctx->htree_slack_percentage > 100)
			ctx->htree_slack_percentage = 20;
	}

	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
		csum_size = sizeof(struct ext2_dir_entry_tail);

	outdir->max = 0;
	retval = alloc_size_dir(fs, outdir,
				(fd->dir_size / fs->blocksize) + 2);
	if (retval)
		return retval;
	outdir->num = fd->compress ? 0 : 1;
	offset = 0;
	outdir->hashes[0] = 0;
	prev_hash = 1;
	if ((retval = get_next_block(fs, outdir, &block_start)))
		return retval;
	dirent = (struct ext2_dir_entry *) block_start;
	prev_rec_len = 0;
	rec_len = 0;
	left = fs->blocksize - csum_size;
	slack = fd->compress ? 12 :
		((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100;
	if (slack < 12)
		slack = 12;
	for (i = 0; i < fd->num_array; i++) {
		ent = fd->harray + i;
		if (ent->dir->inode == 0)
			continue;
		rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir));
		if (rec_len > left) {
			if (left) {
				left += prev_rec_len;
				retval = ext2fs_set_rec_len(fs, left, dirent);
				if (retval)
					return retval;
			}
			if (csum_size) {
				t = EXT2_DIRENT_TAIL(block_start,
						     fs->blocksize);
				ext2fs_initialize_dirent_tail(fs, t);
			}
			if ((retval = get_next_block(fs, outdir,
						      &block_start)))
				return retval;
			offset = 0;
		}
		left = (fs->blocksize - csum_size) - offset;
		dirent = (struct ext2_dir_entry *) (block_start + offset);
		if (offset == 0) {
			if (ent->hash == prev_hash)
				outdir->hashes[outdir->num-1] = ent->hash | 1;
			else
				outdir->hashes[outdir->num-1] = ent->hash;
		}
		dirent->inode = ent->dir->inode;
		ext2fs_dirent_set_name_len(dirent,
					   ext2fs_dirent_name_len(ent->dir));
		ext2fs_dirent_set_file_type(dirent,
					    ext2fs_dirent_file_type(ent->dir));
		retval = ext2fs_set_rec_len(fs, rec_len, dirent);
		if (retval)
			return retval;
		prev_rec_len = rec_len;
		memcpy(dirent->name, ent->dir->name,
		       ext2fs_dirent_name_len(dirent));
		offset += rec_len;
		left -= rec_len;
		if (left < slack) {
			prev_rec_len += left;
			retval = ext2fs_set_rec_len(fs, prev_rec_len, dirent);
			if (retval)
				return retval;
			offset += left;
			left = 0;
		}
		prev_hash = ent->hash;
	}
	if (left)
		retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);
	if (csum_size) {
		t = EXT2_DIRENT_TAIL(block_start, fs->blocksize);
		ext2fs_initialize_dirent_tail(fs, t);
	}

	return retval;
}
Esempio n. 26
0
static errcode_t copy_dir_entries(e2fsck_t ctx,
				  struct fill_dir_struct *fd,
				  struct out_dir *outdir)
{
	ext2_filsys		fs = ctx->fs;
	errcode_t		retval;
	char			*block_start;
	struct hash_entry	*ent;
	struct ext2_dir_entry	*dirent;
	unsigned int		rec_len, prev_rec_len;
	int			i, left;
	ext2_dirhash_t		prev_hash;
	int			offset, slack;

	if (ctx->htree_slack_percentage == 255) {
		profile_get_uint(ctx->profile, "options",
				 "indexed_dir_slack_percentage",
				 0, 20,
				 &ctx->htree_slack_percentage);
		if (ctx->htree_slack_percentage > 100)
			ctx->htree_slack_percentage = 20;
	}

	outdir->max = 0;
	retval = alloc_size_dir(fs, outdir,
				(fd->dir_size / fs->blocksize) + 2);
	if (retval)
		return retval;
	outdir->num = fd->compress ? 0 : 1;
	offset = 0;
	outdir->hashes[0] = 0;
	prev_hash = 1;
	if ((retval = get_next_block(fs, outdir, &block_start)))
		return retval;
	dirent = (struct ext2_dir_entry *) block_start;
	prev_rec_len = 0;
	left = fs->blocksize;
	slack = fd->compress ? 12 :
		(fs->blocksize * ctx->htree_slack_percentage)/100;
	if (slack < 12)
		slack = 12;
	for (i=0; i < fd->num_array; i++) {
		ent = fd->harray + i;
		if (ent->dir->inode == 0)
			continue;
		rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
		if (rec_len > left) {
			if (left) {
				left += prev_rec_len;
				retval = ext2fs_set_rec_len(fs, left, dirent);
				if (retval)
					return retval;
			}
			if ((retval = get_next_block(fs, outdir,
						      &block_start)))
				return retval;
			offset = 0;
		}
		left = fs->blocksize - offset;
		dirent = (struct ext2_dir_entry *) (block_start + offset);
		if (offset == 0) {
			if (ent->hash == prev_hash)
				outdir->hashes[outdir->num-1] = ent->hash | 1;
			else
				outdir->hashes[outdir->num-1] = ent->hash;
		}
		dirent->inode = ent->dir->inode;
		dirent->name_len = ent->dir->name_len;
		retval = ext2fs_set_rec_len(fs, rec_len, dirent);
		if (retval)
			return retval;
		prev_rec_len = rec_len;
		memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
		offset += rec_len;
		left -= rec_len;
		if (left < slack) {
			prev_rec_len += left;
			retval = ext2fs_set_rec_len(fs, prev_rec_len, dirent);
			if (retval)
				return retval;
			offset += left;
			left = 0;
		}
		prev_hash = ent->hash;
	}
	if (left)
		retval = ext2fs_set_rec_len(fs, rec_len + left, dirent);

	return retval;
}
Esempio n. 27
0
/*
 * Create new directory block
 */
errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
			       ext2_ino_t parent_ino, char **block)
{
	struct ext2_dir_entry 	*dir = NULL;
	errcode_t		retval;
	char			*buf;
	int			rec_len;
	int			filetype = 0;
	struct ext2_dir_entry_tail	*t;
	int			csum_size = 0;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	retval = ext2fs_get_mem(fs->blocksize, &buf);
	if (retval)
		return retval;
	memset(buf, 0, fs->blocksize);
	dir = (struct ext2_dir_entry *) buf;

	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
		csum_size = sizeof(struct ext2_dir_entry_tail);

	retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir);
	if (retval)
		return retval;

	if (dir_ino) {
		if (fs->super->s_feature_incompat &
		    EXT2_FEATURE_INCOMPAT_FILETYPE)
			filetype = EXT2_FT_DIR;
		/*
		 * Set up entry for '.'
		 */
		dir->inode = dir_ino;
		ext2fs_dirent_set_name_len(dir, 1);
		ext2fs_dirent_set_file_type(dir, filetype);
		dir->name[0] = '.';
		rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
		dir->rec_len = EXT2_DIR_REC_LEN(1);

		/*
		 * Set up entry for '..'
		 */
		dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
		retval = ext2fs_set_rec_len(fs, rec_len, dir);
		if (retval)
			return retval;
		dir->inode = parent_ino;
		ext2fs_dirent_set_name_len(dir, 2);
		ext2fs_dirent_set_file_type(dir, filetype);
		dir->name[0] = '.';
		dir->name[1] = '.';

	}

	if (csum_size) {
		t = EXT2_DIRENT_TAIL(buf, fs->blocksize);
		ext2fs_initialize_dirent_tail(fs, t);
	}
	*block = buf;
	return 0;
}
Esempio n. 28
0
/*
 * Convert a component of a pathname into a pointer to a locked inode.
 * This is a very central and rather complicated routine.
 * If the file system is not maintained in a strict tree hierarchy,
 * this can result in a deadlock situation (see comments in code below).
 *
 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
 * on whether the name is to be looked up, created, renamed, or deleted.
 * When CREATE, RENAME, or DELETE is specified, information usable in
 * creating, renaming, or deleting a directory entry may be calculated.
 * If flag has LOCKPARENT or'ed into it and the target of the pathname
 * exists, lookup returns both the target and its parent directory locked.
 * When creating or renaming and LOCKPARENT is specified, the target may
 * not be ".".  When deleting and LOCKPARENT is specified, the target may
 * be "."., but the caller must check to ensure it does an vrele and vput
 * instead of two vputs.
 *
 * Overall outline of ufs_lookup:
 *
 *	search for name in directory, to found or notfound
 * notfound:
 *	if creating, return locked directory, leaving info on available slots
 *	else return error
 * found:
 *	if at end of path and deleting, return information to allow delete
 *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
 *	  inode and return info to allow rewrite
 *	if not at end, add name to cache; if at end and neither creating
 *	  nor deleting, add name to cache
 *
 * ext2_lookup(struct vnode *a_dvp, struct vnode **a_vpp,
 *	       struct componentname *a_cnp)
 */
int
ext2_lookup(struct vop_old_lookup_args *ap)
{
	struct vnode *vdp;	/* vnode for directory being searched */
	struct inode *dp;	/* inode for directory being searched */
	struct buf *bp;			/* a buffer of directory entries */
	struct ext2_dir_entry_2 *ep; /* the current directory entry */
	int entryoffsetinblock;		/* offset of ep in bp's buffer */
	enum {NONE, COMPACT, FOUND} slotstatus;
	doff_t slotoffset;		/* offset of area with free space */
	int slotsize;			/* size of area at slotoffset */
	int slotfreespace;		/* amount of space free in slot */
	int slotneeded;			/* size of the entry we're seeking */
	int numdirpasses;		/* strategy for directory search */
	doff_t endsearch;		/* offset to end directory search */
	doff_t prevoff;			/* prev entry dp->i_offset */
	struct vnode *pdp;		/* saved dp during symlink work */
	struct vnode *tdp;		/* returned by VFS_VGET */
	doff_t enduseful;		/* pointer past last used dir slot */
	u_long bmask;			/* block offset mask */
	int lockparent;			/* 1 => lockparent flag is set */
	int wantparent;			/* 1 => wantparent or lockparent flag */
	int namlen, error;
	struct vnode **vpp = ap->a_vpp;
	struct componentname *cnp = ap->a_cnp;
	struct ucred *cred = cnp->cn_cred;
	int flags = cnp->cn_flags;
	int nameiop = cnp->cn_nameiop;

	int	DIRBLKSIZ = VTOI(ap->a_dvp)->i_e2fs->s_blocksize;

	bp = NULL;
	slotoffset = -1;
	*vpp = NULL;
	vdp = ap->a_dvp;
	dp = VTOI(vdp);
	lockparent = flags & CNP_LOCKPARENT;
	wantparent = flags & (CNP_LOCKPARENT|CNP_WANTPARENT);

	/*
	 * We now have a segment name to search for, and a directory to search.
	 */

	/*
	 * Suppress search for slots unless creating
	 * file and at end of pathname, in which case
	 * we watch for a place to put the new file in
	 * case it doesn't already exist.
	 */
	slotstatus = FOUND;
	slotfreespace = slotsize = slotneeded = 0;
	if (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) {
		slotstatus = NONE;
		slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
		/* was
		slotneeded = (sizeof(struct direct) - MAXNAMLEN +
			cnp->cn_namelen + 3) &~ 3; */
	}

	/*
	 * If there is cached information on a previous search of
	 * this directory, pick up where we last left off.
	 * We cache only lookups as these are the most common
	 * and have the greatest payoff. Caching CREATE has little
	 * benefit as it usually must search the entire directory
	 * to determine that the entry does not exist. Caching the
	 * location of the last DELETE or RENAME has not reduced
	 * profiling time and hence has been removed in the interest
	 * of simplicity.
	 */
	bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
	if (nameiop != NAMEI_LOOKUP || dp->i_diroff == 0 ||
	    dp->i_diroff > dp->i_size) {
		entryoffsetinblock = 0;
		dp->i_offset = 0;
		numdirpasses = 1;
	} else {
		dp->i_offset = dp->i_diroff;
		if ((entryoffsetinblock = dp->i_offset & bmask) &&
		    (error = EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
			return (error);
		numdirpasses = 2;
	}
	prevoff = dp->i_offset;
	endsearch = roundup(dp->i_size, DIRBLKSIZ);
	enduseful = 0;

searchloop:
	while (dp->i_offset < endsearch) {
		/*
		 * If necessary, get the next directory block.
		 */
		if ((dp->i_offset & bmask) == 0) {
			if (bp != NULL)
				brelse(bp);
			if ((error =
			    EXT2_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)) != 0)
				return (error);
			entryoffsetinblock = 0;
		}
		/*
		 * If still looking for a slot, and at a DIRBLKSIZE
		 * boundary, have to start looking for free space again.
		 */
		if (slotstatus == NONE &&
		    (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
			slotoffset = -1;
			slotfreespace = 0;
		}
		/*
		 * Get pointer to next entry.
		 * Full validation checks are slow, so we only check
		 * enough to insure forward progress through the
		 * directory. Complete checks can be run by patching
		 * "dirchk" to be true.
		 */
		ep = (struct ext2_dir_entry_2 *)
			((char *)bp->b_data + entryoffsetinblock);
		if (ep->rec_len == 0 ||
		    (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) {
			int i;
			ext2_dirbad(dp, dp->i_offset, "mangled entry");
			i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
			dp->i_offset += i;
			entryoffsetinblock += i;
			continue;
		}

		/*
		 * If an appropriate sized slot has not yet been found,
		 * check to see if one is available. Also accumulate space
		 * in the current block so that we can determine if
		 * compaction is viable.
		 */
		if (slotstatus != FOUND) {
			int size = ep->rec_len;

			if (ep->inode != 0)
				size -= EXT2_DIR_REC_LEN(ep->name_len);
			if (size > 0) {
				if (size >= slotneeded) {
					slotstatus = FOUND;
					slotoffset = dp->i_offset;
					slotsize = ep->rec_len;
				} else if (slotstatus == NONE) {
					slotfreespace += size;
					if (slotoffset == -1)
						slotoffset = dp->i_offset;
					if (slotfreespace >= slotneeded) {
						slotstatus = COMPACT;
						slotsize = dp->i_offset +
						      ep->rec_len - slotoffset;
					}
				}
			}
		}

		/*
		 * Check for a name match.
		 */
		if (ep->inode) {
			namlen = ep->name_len;
			if (namlen == cnp->cn_namelen &&
			    !bcmp(cnp->cn_nameptr, ep->name,
				(unsigned)namlen)) {
				/*
				 * Save directory entry's inode number and
				 * reclen in ndp->ni_ufs area, and release
				 * directory buffer.
				 */
				dp->i_ino = ep->inode;
				dp->i_reclen = ep->rec_len;
				goto found;
			}
		}
		prevoff = dp->i_offset;
		dp->i_offset += ep->rec_len;
		entryoffsetinblock += ep->rec_len;
		if (ep->inode)
			enduseful = dp->i_offset;
	}
/* notfound: */
	/*
	 * If we started in the middle of the directory and failed
	 * to find our target, we must check the beginning as well.
	 */
	if (numdirpasses == 2) {
		numdirpasses--;
		dp->i_offset = 0;
		endsearch = dp->i_diroff;
		goto searchloop;
	}
	if (bp != NULL)
		brelse(bp);
	/*
	 * If creating, and at end of pathname and current
	 * directory has not been removed, then can consider
	 * allowing file to be created.
	 */
	if ((nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) &&
	    dp->i_nlink != 0) {
		/*
		 * Access for write is interpreted as allowing
		 * creation of files in the directory.
		 */
		if ((error = VOP_EACCESS(vdp, VWRITE, cred)) != 0)
			return (error);
		/*
		 * Return an indication of where the new directory
		 * entry should be put.  If we didn't find a slot,
		 * then set dp->i_count to 0 indicating
		 * that the new slot belongs at the end of the
		 * directory. If we found a slot, then the new entry
		 * can be put in the range from dp->i_offset to
		 * dp->i_offset + dp->i_count.
		 */
		if (slotstatus == NONE) {
			dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
			dp->i_count = 0;
			enduseful = dp->i_offset;
		} else {
			dp->i_offset = slotoffset;
			dp->i_count = slotsize;
			if (enduseful < slotoffset + slotsize)
				enduseful = slotoffset + slotsize;
		}
		dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
		/*
		 * We return with the directory locked, so that
		 * the parameters we set up above will still be
		 * valid if we actually decide to do a direnter().
		 * We return ni_vp == NULL to indicate that the entry
		 * does not currently exist; we leave a pointer to
		 * the (locked) directory inode in ndp->ni_dvp.
		 * The pathname buffer is saved so that the name
		 * can be obtained later.
		 *
		 * NB - if the directory is unlocked, then this
		 * information cannot be used.
		 */
		if (!lockparent)
			vn_unlock(vdp);
		return (EJUSTRETURN);
	}
	return (ENOENT);

found:
	/*
	 * Check that directory length properly reflects presence
	 * of this entry.
	 */
	if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->name_len)
		> dp->i_size) {
		ext2_dirbad(dp, dp->i_offset, "i_size too small");
		dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->name_len);
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
	}
	brelse(bp);

	/*
	 * Found component in pathname.
	 * If the final component of path name, save information
	 * in the cache as to where the entry was found.
	 */
	if (nameiop == NAMEI_LOOKUP)
		dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);

	/*
	 * If deleting, and at end of pathname, return
	 * parameters which can be used to remove file.
	 * If the wantparent flag isn't set, we return only
	 * the directory (in ndp->ni_dvp), otherwise we go
	 * on and lock the inode, being careful with ".".
	 */
	if (nameiop == NAMEI_DELETE) {
		/*
		 * Write access to directory required to delete files.
		 */
		if ((error = VOP_EACCESS(vdp, VWRITE, cred)) != 0)
			return (error);
		/*
		 * Return pointer to current entry in dp->i_offset,
		 * and distance past previous entry (if there
		 * is a previous entry in this block) in dp->i_count.
		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
		 */
		if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
			dp->i_count = 0;
		else
			dp->i_count = dp->i_offset - prevoff;
		if (dp->i_number == dp->i_ino) {
			vref(vdp);
			*vpp = vdp;
			return (0);
		}
		if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0)
			return (error);
		/*
		 * If directory is "sticky", then user must own
		 * the directory, or the file in it, else she
		 * may not delete it (unless she's root). This
		 * implements append-only directories.
		 */
		if ((dp->i_mode & ISVTX) &&
		    cred->cr_uid != 0 &&
		    cred->cr_uid != dp->i_uid &&
		    VTOI(tdp)->i_uid != cred->cr_uid) {
			vput(tdp);
			return (EPERM);
		}
		*vpp = tdp;
		if (!lockparent)
			vn_unlock(vdp);
		return (0);
	}

	/*
	 * If rewriting (RENAME), return the inode and the
	 * information required to rewrite the present directory
	 * Must get inode of directory entry to verify it's a
	 * regular file, or empty directory.
	 */
	if (nameiop == NAMEI_RENAME && wantparent) {
		if ((error = VOP_EACCESS(vdp, VWRITE, cred)) != 0)
			return (error);
		/*
		 * Careful about locking second inode.
		 * This can only occur if the target is ".".
		 */
		if (dp->i_number == dp->i_ino)
			return (EISDIR);
		if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0)
			return (error);
		*vpp = tdp;
		if (!lockparent)
			vn_unlock(vdp);
		return (0);
	}

	/*
	 * Step through the translation in the name.  We do not `vput' the
	 * directory because we may need it again if a symbolic link
	 * is relative to the current directory.  Instead we save it
	 * unlocked as "pdp".  We must get the target inode before unlocking
	 * the directory to insure that the inode will not be removed
	 * before we get it.  We prevent deadlock by always fetching
	 * inodes from the root, moving down the directory tree. Thus
	 * when following backward pointers ".." we must unlock the
	 * parent directory before getting the requested directory.
	 * There is a potential race condition here if both the current
	 * and parent directories are removed before the VFS_VGET for the
	 * inode associated with ".." returns.  We hope that this occurs
	 * infrequently since we cannot avoid this race condition without
	 * implementing a sophisticated deadlock detection algorithm.
	 * Note also that this simple deadlock detection scheme will not
	 * work if the file system has any hard links other than ".."
	 * that point backwards in the directory structure.
	 */
	pdp = vdp;
	if (flags & CNP_ISDOTDOT) {
		vn_unlock(pdp);	/* race to get the inode */
		if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0) {
			vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY);
			return (error);
		}
		if (lockparent && (error = vn_lock(pdp, LK_EXCLUSIVE))) {
			vput(tdp);
			return (error);
		}
		*vpp = tdp;
	} else if (dp->i_number == dp->i_ino) {
		vref(vdp);	/* we want ourself, ie "." */
		*vpp = vdp;
	} else {
		if ((error = VFS_VGET(vdp->v_mount, NULL, dp->i_ino, &tdp)) != 0)
			return (error);
		if (!lockparent)
			vn_unlock(pdp);
		*vpp = tdp;
	}
	return (0);
}