Module Name: src
Committed By: christos
Date: Fri Jun 24 17:21:30 UTC 2016
Modified Files:
src/sys/ufs/ext2fs: ext2fs.h ext2fs_dir.h ext2fs_extern.h
ext2fs_lookup.c
Added Files:
src/sys/ufs/ext2fs: ext2fs_hash.c ext2fs_hash.h ext2fs_htree.c
ext2fs_htree.h
Log Message:
GSoC 2016 (Hrishikesh Goyal): Htree index support from FreeBSD
To generate a diff of this commit:
cvs rdiff -u -r1.37 -r1.38 src/sys/ufs/ext2fs/ext2fs.h
cvs rdiff -u -r1.19 -r1.20 src/sys/ufs/ext2fs/ext2fs_dir.h
cvs rdiff -u -r1.48 -r1.49 src/sys/ufs/ext2fs/ext2fs_extern.h
cvs rdiff -u -r0 -r1.1 src/sys/ufs/ext2fs/ext2fs_hash.c \
src/sys/ufs/ext2fs/ext2fs_hash.h src/sys/ufs/ext2fs/ext2fs_htree.c \
src/sys/ufs/ext2fs/ext2fs_htree.h
cvs rdiff -u -r1.79 -r1.80 src/sys/ufs/ext2fs/ext2fs_lookup.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/ufs/ext2fs/ext2fs.h
diff -u src/sys/ufs/ext2fs/ext2fs.h:1.37 src/sys/ufs/ext2fs/ext2fs.h:1.38
--- src/sys/ufs/ext2fs/ext2fs.h:1.37 Fri Jun 3 11:35:48 2016
+++ src/sys/ufs/ext2fs/ext2fs.h Fri Jun 24 13:21:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: ext2fs.h,v 1.37 2016/06/03 15:35:48 christos Exp $ */
+/* $NetBSD: ext2fs.h,v 1.38 2016/06/24 17:21:30 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
@@ -235,6 +235,7 @@ struct m_ext2fs {
u_char e2fs_fsmnt[MAXMNTLEN]; /* name mounted on */
int8_t e2fs_ronly; /* mounted read-only flag */
int8_t e2fs_fmod; /* super block modified flag */
+ int8_t e2fs_uhash; /* 3 if hash should be signed, 0 if not */
int32_t e2fs_bsize; /* block size */
int32_t e2fs_bshift; /* ``lblkno'' calc of logical blkno */
int32_t e2fs_bmask; /* ``blkoff'' calc of blk offsets */
@@ -331,6 +332,12 @@ struct m_ext2fs {
| EXT2F_INCOMPAT_EXTENTS)
/*
+ * Feature set definitions
+ */
+#define EXT2_HAS_COMPAT_FEATURE(sb, mask) \
+ ((sb)->e2fs.e2fs_features_compat & htole32(mask))
+
+/*
* Definitions of behavior on errors
*/
#define E2FS_BEH_CONTINUE 1 /* continue operation */
Index: src/sys/ufs/ext2fs/ext2fs_dir.h
diff -u src/sys/ufs/ext2fs/ext2fs_dir.h:1.19 src/sys/ufs/ext2fs/ext2fs_dir.h:1.20
--- src/sys/ufs/ext2fs/ext2fs_dir.h:1.19 Tue May 8 20:21:18 2012
+++ src/sys/ufs/ext2fs/ext2fs_dir.h Fri Jun 24 13:21:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: ext2fs_dir.h,v 1.19 2012/05/09 00:21:18 riastradh Exp $ */
+/* $NetBSD: ext2fs_dir.h,v 1.20 2016/06/24 17:21:30 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@@ -114,6 +114,20 @@ struct ext2fs_direct {
char e2d_name[EXT2FS_MAXNAMLEN];/* name with length<=EXT2FS_MAXNAMLEN */
};
+enum ext2fs_slotstatus {
+ NONE,
+ COMPACT,
+ FOUND
+};
+
+struct ext2fs_searchslot {
+ enum ext2fs_slotstatus 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; /* sizeof the entry we are seeking */
+};
+
/* Ext2 directory file types (not the same as FFS. Sigh.) */
#define EXT2_FT_UNKNOWN 0
#define EXT2_FT_REG_FILE 1
@@ -179,4 +193,14 @@ struct ext2fs_dirtemplate {
char dotdot_name[4]; /* ditto */
};
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(namelen) \
+ (((namelen) + 8 + EXT2_DIR_ROUND) & ~EXT2_DIR_ROUND)
+
#endif /* !_UFS_EXT2FS_EXT2FS_DIR_H_ */
Index: src/sys/ufs/ext2fs/ext2fs_extern.h
diff -u src/sys/ufs/ext2fs/ext2fs_extern.h:1.48 src/sys/ufs/ext2fs/ext2fs_extern.h:1.49
--- src/sys/ufs/ext2fs/ext2fs_extern.h:1.48 Fri Mar 27 13:27:56 2015
+++ src/sys/ufs/ext2fs/ext2fs_extern.h Fri Jun 24 13:21:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: ext2fs_extern.h,v 1.48 2015/03/27 17:27:56 riastradh Exp $ */
+/* $NetBSD: ext2fs_extern.h,v 1.49 2016/06/24 17:21:30 christos Exp $ */
/*-
* Copyright (c) 1991, 1993, 1994
@@ -78,6 +78,7 @@ struct vnode;
struct mbuf;
struct componentname;
struct ufs_lookup_results;
+struct ext2fs_searchslot;
extern struct pool ext2fs_inode_pool; /* memory pool for inodes */
extern struct pool ext2fs_dinode_pool; /* memory pool for dinodes */
@@ -120,6 +121,9 @@ int ext2fs_inactive(void *);
/* ext2fs_lookup.c */
int ext2fs_readdir(void *);
int ext2fs_lookup(void *);
+int ext2fs_search_dirblock(struct inode *, void *, int *,
+ const char *, int , int *, doff_t *, doff_t *, doff_t *,
+ struct ext2fs_searchslot *);
int ext2fs_direnter(struct inode *, struct vnode *,
const struct ufs_lookup_results *,
struct componentname *);
@@ -172,6 +176,15 @@ int ext2fs_makeinode(int, struct vnode *
struct componentname *cnp);
int ext2fs_reclaim(void *);
+/* ext2fs_hash.c */
+int ext2fs_htree_hash(const char *, int, uint32_t *, int, uint32_t *,
+ uint32_t *);
+
+/* ext2fs_htree.c */
+int ext2fs_htree_has_idx(struct inode *);
+int ext2fs_htree_lookup(struct inode *, const char *, int, struct buf **,
+ int *, doff_t *, doff_t *, doff_t *, struct ext2fs_searchslot *);
+
__END_DECLS
#define IS_EXT2_VNODE(vp) (vp->v_tag == VT_EXT2FS)
Index: src/sys/ufs/ext2fs/ext2fs_lookup.c
diff -u src/sys/ufs/ext2fs/ext2fs_lookup.c:1.79 src/sys/ufs/ext2fs/ext2fs_lookup.c:1.80
--- src/sys/ufs/ext2fs/ext2fs_lookup.c:1.79 Tue Jan 12 16:29:29 2016
+++ src/sys/ufs/ext2fs/ext2fs_lookup.c Fri Jun 24 13:21:30 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: ext2fs_lookup.c,v 1.79 2016/01/12 21:29:29 riastradh Exp $ */
+/* $NetBSD: ext2fs_lookup.c,v 1.80 2016/06/24 17:21:30 christos Exp $ */
/*
* Modified for NetBSD 1.2E
@@ -48,7 +48,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.79 2016/01/12 21:29:29 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.80 2016/06/24 17:21:30 christos Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -70,6 +70,7 @@ __KERNEL_RCSID(0, "$NetBSD: ext2fs_looku
#include <ufs/ext2fs/ext2fs_extern.h>
#include <ufs/ext2fs/ext2fs_dir.h>
#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_htree.h>
#include <miscfs/genfs/genfs.h>
@@ -117,6 +118,13 @@ ext2fs_dirconv2ffs(struct ext2fs_direct
ffsdir->d_reclen = _DIRENT_SIZE(ffsdir);
}
+static int
+ext2fs_is_dot_entry(struct componentname *cnp)
+{
+ return cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' &&
+ (cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '\0');
+}
+
/*
* Vnode op for reading directories.
*
@@ -271,7 +279,7 @@ ext2fs_lookup(void *v)
struct buf *bp; /* a buffer of directory entries */
struct ext2fs_direct *ep; /* the current directory entry */
int entryoffsetinblock; /* offset of ep in bp's buffer */
- enum {NONE, COMPACT, FOUND} slotstatus;
+ enum ext2fs_slotstatus 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 */
@@ -292,6 +300,8 @@ ext2fs_lookup(void *v)
int dirblksiz = ump->um_dirblksiz;
ino_t foundino;
struct ufs_lookup_results *results;
+ doff_t i_offset; /* cached i_offset value */
+ struct ext2fs_searchslot ss;
flags = cnp->cn_flags;
@@ -372,6 +382,37 @@ ext2fs_lookup(void *v)
endsearch = roundup(ext2fs_size(dp), dirblksiz);
enduseful = 0;
+ /*
+ * Try to lookup dir entry using htree directory index.
+ *
+ * If we got an error or we want to find '.' or '..' entry,
+ * we will fall back to linear search.
+ */
+ if (ext2fs_htree_has_idx(dp) && ext2fs_is_dot_entry(cnp)) {
+ numdirpasses = 1;
+ entryoffsetinblock = 0;
+
+ int htree_lookup_ret = ext2fs_htree_lookup(dp, cnp->cn_nameptr,
+ cnp->cn_namelen, &bp, &entryoffsetinblock, &i_offset,
+ &prevoff, &enduseful, &ss);
+ switch (htree_lookup_ret) {
+ case 0:
+ ep = (struct ext2fs_direct*)((char *)bp->b_data +
+ (i_offset & bmask));
+ foundino = ep->e2d_ino;
+ goto found;
+ case ENOENT:
+ i_offset = roundup2(dp->i_size, dp->i_e2fs->e2fs_bsize);
+ goto notfound;
+ default:
+ /*
+ * Something failed; just fallback to do a linear
+ * search.
+ */
+ break;
+ }
+ }
+
searchloop:
while (results->ulr_offset < endsearch) {
if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
@@ -473,7 +514,7 @@ searchloop:
if (ep->e2d_ino)
enduseful = results->ulr_offset;
}
-/* notfound: */
+notfound:
/*
* If we started in the middle of the directory and failed
* to find our target, we must check the beginning as well.
@@ -666,6 +707,96 @@ found:
return 0;
}
+int
+ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp,
+ const char *name, int namelen, int *entryoffsetinblockp,
+ doff_t *offp, doff_t *prevoffp, doff_t *endusefulp,
+ struct ext2fs_searchslot *ssp)
+{
+ struct vnode *vdp = ITOV(ip);
+ struct ext2fs_direct *ep, *top;
+ uint32_t bsize = ip->i_e2fs->e2fs_bsize;
+ int offset = *entryoffsetinblockp;
+ int namlen;
+
+ ep = (void *)((char *)data + offset);
+ top = (void *)((char *)data + bsize - EXT2_DIR_REC_LEN(0));
+
+ while (ep < top) {
+ /*
+ * Full validation checks are slow, so we only check
+ * enough to insure forward progress through the
+ * directory. Complete checks can be run by setting
+ * "vfs.e2fs.dirchk" to be true.
+ */
+ if (ep->e2d_reclen == 0 ||
+ (dirchk && ext2fs_dirbadentry(vdp, ep, offset))) {
+ int i;
+ ufs_dirbad(ip, *offp, "mangled entry");
+ i = bsize - (offset & (bsize - 1));
+ *offp += i;
+ offset += 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 (ssp->slotstatus != FOUND) {
+ int size = ep->e2d_reclen;
+
+ if (ep->e2d_ino != 0)
+ size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
+ if (size >= ssp->slotneeded) {
+ ssp->slotstatus = FOUND;
+ ssp->slotoffset = *offp;
+ ssp->slotsize = ep->e2d_reclen;
+ } else if (size > 0 && ssp->slotstatus == NONE) {
+ ssp->slotfreespace += size;
+ if (ssp->slotoffset == -1)
+ ssp->slotoffset = *offp;
+ if (ssp->slotfreespace >= ssp->slotneeded) {
+ ssp->slotstatus = COMPACT;
+ ssp->slotsize = *offp + ep->e2d_reclen -
+ ssp->slotoffset;
+ }
+ }
+ }
+
+ /*
+ * Check for a name match.
+ */
+ if (ep->e2d_ino) {
+ namlen = ep->e2d_namlen;
+ if (namlen == namelen &&
+ !memcmp(name, ep->e2d_name, (unsigned)namlen)) {
+ /*
+ * Save directory entry's inode number and
+ * reclen in ndp->ni_ufs area, and release
+ * directory buffer.
+ */
+ *foundp = 1;
+ return 0;
+ }
+ }
+ *prevoffp = *offp;
+ *offp += ep->e2d_reclen;
+ offset += ep->e2d_reclen;
+ *entryoffsetinblockp = offset;
+ if (ep->e2d_ino)
+ *endusefulp = *offp;
+ /*
+ * Get pointer to the next entry.
+ */
+ ep = (void *)((char *)data + offset);
+ }
+
+ return 0;
+}
+
/*
* Do consistency checking on a directory entry:
* record length must be multiple of 4
Added files:
Index: src/sys/ufs/ext2fs/ext2fs_hash.c
diff -u /dev/null src/sys/ufs/ext2fs/ext2fs_hash.c:1.1
--- /dev/null Fri Jun 24 13:21:30 2016
+++ src/sys/ufs/ext2fs/ext2fs_hash.c Fri Jun 24 13:21:30 2016
@@ -0,0 +1,314 @@
+/* $NetBSD: ext2fs_hash.c,v 1.1 2016/06/24 17:21:30 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010, 2013 Zheng Liu <[email protected]>
+ * Copyright (c) 2012, Vyacheslav Matyushin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/sys/fs/ext2fs/ext2_hash.c 294504 2016-01-21 14:50:28Z pfg $
+ */
+
+/*
+ * The following notice applies to the code in ext2_half_md4():
+ *
+ * Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD4 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_hash.c,v 1.1 2016/06/24 17:21:30 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <ufs/ext2fs/ext2fs_htree.h>
+#include <ufs/ext2fs/ext2fs_hash.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+/*
+ * FF, GG, and HH are transformations for rounds 1, 2, and 3.
+ * Rotation is separated from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s) { \
+ (a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+}
+
+#define GG(a, b, c, d, x, s) { \
+ (a) += G ((b), (c), (d)) + (x) + (uint32_t)0x5A827999; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+}
+
+#define HH(a, b, c, d, x, s) { \
+ (a) += H ((b), (c), (d)) + (x) + (uint32_t)0x6ED9EBA1; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+}
+
+static void
+ext2fs_prep_hashbuf(const char *src, int slen, uint32_t *dst, int dlen,
+ int unsigned_char)
+{
+ uint32_t padding = slen | (slen << 8) | (slen << 16) | (slen << 24);
+ uint32_t buf_val;
+ const unsigned char *ubuf = (const unsigned char *)src;
+ const signed char *sbuf = (const signed char *)src;
+ int len, i;
+ int buf_byte;
+
+ if (slen > dlen)
+ len = dlen;
+ else
+ len = slen;
+
+ buf_val = padding;
+
+ for (i = 0; i < len; i++) {
+ if (unsigned_char)
+ buf_byte = (u_int)ubuf[i];
+ else
+ buf_byte = (int)sbuf[i];
+
+ if ((i % 4) == 0)
+ buf_val = padding;
+
+ buf_val <<= 8;
+ buf_val += buf_byte;
+
+ if ((i % 4) == 3) {
+ *dst++ = buf_val;
+ dlen -= sizeof(uint32_t);
+ buf_val = padding;
+ }
+ }
+
+ dlen -= sizeof(uint32_t);
+ if (dlen >= 0)
+ *dst++ = buf_val;
+
+ dlen -= sizeof(uint32_t);
+ while (dlen >= 0) {
+ *dst++ = padding;
+ dlen -= sizeof(uint32_t);
+ }
+}
+
+static uint32_t
+ext2fs_legacy_hash(const char *name, int len, int unsigned_char)
+{
+ uint32_t h0, h1 = 0x12A3FE2D, h2 = 0x37ABE8F9;
+ uint32_t multi = 0x6D22F5;
+ const unsigned char *uname = (const unsigned char *)name;
+ const signed char *sname = (const signed char *)name;
+ int val, i;
+
+ for (i = 0; i < len; i++) {
+ if (unsigned_char)
+ val = (u_int)*uname++;
+ else
+ val = (int)*sname++;
+
+ h0 = h2 + (h1 ^ (val * multi));
+ if (h0 & 0x80000000)
+ h0 -= 0x7FFFFFFF;
+ h2 = h1;
+ h1 = h0;
+ }
+
+ return h1 << 1;
+}
+
+/*
+ * MD4 basic transformation. It transforms state based on block.
+ *
+ * This is a half md4 algorithm since Linux uses this algorithm for dir
+ * index. This function is derived from the RSA Data Security, Inc. MD4
+ * Message-Digest Algorithm and was modified as necessary.
+ *
+ * The return value of this function is uint32_t in Linux, but actually we don't
+ * need to check this value, so in our version this function doesn't return any
+ * value.
+ */
+static void
+ext2fs_half_md4(uint32_t hash[4], uint32_t data[8])
+{
+ uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3];
+
+ /* Round 1 */
+ FF(a, b, c, d, data[0], 3);
+ FF(d, a, b, c, data[1], 7);
+ FF(c, d, a, b, data[2], 11);
+ FF(b, c, d, a, data[3], 19);
+ FF(a, b, c, d, data[4], 3);
+ FF(d, a, b, c, data[5], 7);
+ FF(c, d, a, b, data[6], 11);
+ FF(b, c, d, a, data[7], 19);
+
+ /* Round 2 */
+ GG(a, b, c, d, data[1], 3);
+ GG(d, a, b, c, data[3], 5);
+ GG(c, d, a, b, data[5], 9);
+ GG(b, c, d, a, data[7], 13);
+ GG(a, b, c, d, data[0], 3);
+ GG(d, a, b, c, data[2], 5);
+ GG(c, d, a, b, data[4], 9);
+ GG(b, c, d, a, data[6], 13);
+
+ /* Round 3 */
+ HH(a, b, c, d, data[3], 3);
+ HH(d, a, b, c, data[7], 9);
+ HH(c, d, a, b, data[2], 11);
+ HH(b, c, d, a, data[6], 15);
+ HH(a, b, c, d, data[1], 3);
+ HH(d, a, b, c, data[5], 9);
+ HH(c, d, a, b, data[0], 11);
+ HH(b, c, d, a, data[4], 15);
+
+ hash[0] += a;
+ hash[1] += b;
+ hash[2] += c;
+ hash[3] += d;
+}
+
+/*
+ * Tiny Encryption Algorithm.
+ */
+static void
+ext2fs_tea(uint32_t hash[4], uint32_t data[8])
+{
+ uint32_t tea_delta = 0x9E3779B9;
+ uint32_t sum;
+ uint32_t x = hash[0], y = hash[1];
+ int n = 16;
+ int i = 1;
+
+ while (n-- > 0) {
+ sum = i * tea_delta;
+ x += ((y << 4) + data[0]) ^ (y + sum) ^ ((y >> 5) + data[1]);
+ y += ((x << 4) + data[2]) ^ (x + sum) ^ ((x >> 5) + data[3]);
+ i++;
+ }
+
+ hash[0] += x;
+ hash[1] += y;
+}
+
+int
+ext2fs_htree_hash(const char *name, int len,
+ uint32_t *hash_seed, int hash_version,
+ uint32_t *hash_major, uint32_t *hash_minor)
+{
+ uint32_t hash[4];
+ uint32_t data[8];
+ uint32_t major = 0, minor = 0;
+ int unsigned_char = 0;
+
+ if (!name || !hash_major)
+ return (-1);
+
+ if (len < 1 || len > 255)
+ goto error;
+
+ hash[0] = 0x67452301;
+ hash[1] = 0xEFCDAB89;
+ hash[2] = 0x98BADCFE;
+ hash[3] = 0x10325476;
+
+ if (hash_seed)
+ memcpy(hash, hash_seed, sizeof(hash));
+
+ switch (hash_version) {
+ case EXT2_HTREE_TEA_UNSIGNED:
+ unsigned_char = 1;
+ /* FALLTHROUGH */
+ case EXT2_HTREE_TEA:
+ while (len > 0) {
+ ext2fs_prep_hashbuf(name, len, data, 16, unsigned_char);
+ ext2fs_tea(hash, data);
+ len -= 16;
+ name += 16;
+ }
+ major = hash[0];
+ minor = hash[1];
+ break;
+ case EXT2_HTREE_LEGACY_UNSIGNED:
+ unsigned_char = 1;
+ /* FALLTHROUGH */
+ case EXT2_HTREE_LEGACY:
+ major = ext2fs_legacy_hash(name, len, unsigned_char);
+ break;
+ case EXT2_HTREE_HALF_MD4_UNSIGNED:
+ unsigned_char = 1;
+ /* FALLTHROUGH */
+ case EXT2_HTREE_HALF_MD4:
+ while (len > 0) {
+ ext2fs_prep_hashbuf(name, len, data, 32, unsigned_char);
+ ext2fs_half_md4(hash, data);
+ len -= 32;
+ name += 32;
+ }
+ major = hash[1];
+ minor = hash[2];
+ break;
+ default:
+ goto error;
+ }
+
+ major &= ~1;
+ if (major == (EXT2_HTREE_EOF << 1))
+ major = (EXT2_HTREE_EOF - 1) << 1;
+ *hash_major = major;
+ if (hash_minor)
+ *hash_minor = minor;
+
+ return 0;
+
+error:
+ *hash_major = 0;
+ if (hash_minor)
+ *hash_minor = 0;
+ return -1;
+}
Index: src/sys/ufs/ext2fs/ext2fs_hash.h
diff -u /dev/null src/sys/ufs/ext2fs/ext2fs_hash.h:1.1
--- /dev/null Fri Jun 24 13:21:30 2016
+++ src/sys/ufs/ext2fs/ext2fs_hash.h Fri Jun 24 13:21:30 2016
@@ -0,0 +1,39 @@
+/* $NetBSD: ext2fs_hash.h,v 1.1 2016/06/24 17:21:30 christos Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _EXT2FS_HASH_H_
+#define _EXT2FS_HASH_H_
+
+/* F, G, and H are MD4 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+#endif /* _EXT2FS_HASH_H_ */
Index: src/sys/ufs/ext2fs/ext2fs_htree.c
diff -u /dev/null src/sys/ufs/ext2fs/ext2fs_htree.c:1.1
--- /dev/null Fri Jun 24 13:21:30 2016
+++ src/sys/ufs/ext2fs/ext2fs_htree.c Fri Jun 24 13:21:30 2016
@@ -0,0 +1,316 @@
+/* $NetBSD: ext2fs_htree.c,v 1.1 2016/06/24 17:21:30 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010, 2012 Zheng Liu <[email protected]>
+ * Copyright (c) 2012, Vyacheslav Matyushin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/sys/fs/ext2fs/ext2_htree.c 294653 2016-01-24 02:41:49Z pfg $
+ */
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_htree.c,v 1.1 2016/06/24 17:21:30 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/lockf.h>
+#include <sys/pool.h>
+#include <sys/signalvar.h>
+#include <sys/kauth.h>
+
+#include <ufs/ufs/dir.h>
+
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+#include <ufs/ext2fs/ext2fs_extern.h>
+#include <ufs/ext2fs/ext2fs_dinode.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs_htree.h>
+#include <ufs/ext2fs/ext2fs_hash.h>
+
+int
+ext2fs_htree_has_idx(struct inode *ip)
+{
+ return EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX)
+ && (ip->i_din.e2fs_din->e2di_flags & EXT2_INDEX);
+}
+
+static off_t
+ext2fs_htree_get_block(struct ext2fs_htree_entry *ep)
+{
+ return ep->h_blk & 0x00FFFFFF;
+}
+
+
+static void
+ext2fs_htree_release(struct ext2fs_htree_lookup_info *info)
+{
+ for (u_int i = 0; i < info->h_levels_num; i++) {
+ struct buf *bp = info->h_levels[i].h_bp;
+ if (bp != NULL)
+ brelse(bp, 0);
+ }
+}
+
+static uint16_t
+ext2fs_htree_get_limit(struct ext2fs_htree_entry *ep)
+{
+ return ((struct ext2fs_htree_count *)(ep))->h_entries_max;
+}
+
+static uint32_t
+ext2fs_htree_root_limit(struct inode *ip, int len)
+{
+ uint32_t space = ip->i_e2fs->e2fs_bsize - EXT2_DIR_REC_LEN(1) -
+ EXT2_DIR_REC_LEN(2) - len;
+ return space / sizeof(struct ext2fs_htree_entry);
+}
+
+static uint16_t
+ext2fs_htree_get_count(struct ext2fs_htree_entry *ep)
+{
+ return ((struct ext2fs_htree_count *)(ep))->h_entries_num;
+}
+
+static uint32_t
+ext2fs_htree_get_hash(struct ext2fs_htree_entry *ep)
+{
+ return ep->h_hash;
+}
+
+static int
+ext2fs_htree_check_next(struct inode *ip, uint32_t hash, const char *name,
+ struct ext2fs_htree_lookup_info *info)
+{
+ struct vnode *vp = ITOV(ip);
+ struct ext2fs_htree_lookup_level *level;
+ struct buf *bp;
+ uint32_t next_hash;
+ int idx = info->h_levels_num - 1;
+ int levels = 0;
+
+ for (;;) {
+ level = &info->h_levels[idx];
+ level->h_entry++;
+ if (level->h_entry < level->h_entries +
+ ext2fs_htree_get_count(level->h_entries))
+ break;
+ if (idx == 0)
+ return 0;
+ idx--;
+ levels++;
+ }
+
+ next_hash = ext2fs_htree_get_hash(level->h_entry);
+ if ((hash & 1) == 0) {
+ if (hash != (next_hash & ~1))
+ return 0;
+ }
+
+ while (levels > 0) {
+ levels--;
+ if (ext2fs_blkatoff(vp, ext2fs_htree_get_block(level->h_entry) *
+ ip->i_e2fs->e2fs_bsize, NULL, &bp) != 0)
+ return 0;
+ level = &info->h_levels[idx + 1];
+ brelse(level->h_bp, 0);
+ level->h_bp = bp;
+ level->h_entry = level->h_entries =
+ ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
+ }
+
+ return 1;
+}
+
+static int
+ext2fs_htree_find_leaf(struct inode *ip, const char *name, int namelen,
+ uint32_t *hash, uint8_t *hash_ver,
+ struct ext2fs_htree_lookup_info *info)
+{
+ struct vnode *vp;
+ struct ext2fs *fs;/* F, G, and H are MD4 functions */
+ struct m_ext2fs *m_fs;
+ struct buf *bp = NULL;
+ struct ext2fs_htree_root *rootp;
+ struct ext2fs_htree_entry *entp, *start, *end, *middle, *found;
+ struct ext2fs_htree_lookup_level *level_info;
+ uint32_t hash_major = 0, hash_minor = 0;
+ uint32_t levels, cnt;
+ uint8_t hash_version;
+
+ if (name == NULL || info == NULL)
+ return -1;
+
+ vp = ITOV(ip);
+ fs = &(ip->i_e2fs->e2fs);
+ m_fs = ip->i_e2fs;
+
+ if (ext2fs_blkatoff(vp, 0, NULL, &bp) != 0)
+ return -1;
+
+ info->h_levels_num = 1;
+ info->h_levels[0].h_bp = bp;
+ rootp = (struct ext2fs_htree_root *)bp->b_data;
+ if (rootp->h_info.h_hash_version != EXT2_HTREE_LEGACY &&
+ rootp->h_info.h_hash_version != EXT2_HTREE_HALF_MD4 &&
+ rootp->h_info.h_hash_version != EXT2_HTREE_TEA)
+ goto error;
+
+ hash_version = rootp->h_info.h_hash_version;
+ if (hash_version <= EXT2_HTREE_TEA)
+ hash_version += m_fs->e2fs_uhash;
+ *hash_ver = hash_version;
+
+ ext2fs_htree_hash(name, namelen, fs->e3fs_hash_seed,
+ hash_version, &hash_major, &hash_minor);
+ *hash = hash_major;
+
+ if ((levels = rootp->h_info.h_ind_levels) > 1)
+ goto error;
+
+ entp = (struct ext2fs_htree_entry *)(((char *)&rootp->h_info) +
+ rootp->h_info.h_info_len);
+
+ if (ext2fs_htree_get_limit(entp) !=
+ ext2fs_htree_root_limit(ip, rootp->h_info.h_info_len))
+ goto error;
+
+ for (;;) {
+ cnt = ext2fs_htree_get_count(entp);
+ if (cnt == 0 || cnt > ext2fs_htree_get_limit(entp))
+ goto error;
+
+ start = entp + 1;
+ end = entp + cnt - 1;
+ while (start <= end) {
+ middle = start + (end - start) / 2;
+ if (ext2fs_htree_get_hash(middle) > hash_major)
+ end = middle - 1;
+ else
+ start = middle + 1;
+ }
+ found = start - 1;
+
+ level_info = &(info->h_levels[info->h_levels_num - 1]);
+ level_info->h_bp = bp;
+ level_info->h_entries = entp;
+ level_info->h_entry = found;
+ if (levels == 0)
+ return (0);
+ levels--;
+ if (ext2fs_blkatoff(vp,
+ ext2fs_htree_get_block(found) * m_fs->e2fs_bsize,
+ NULL, &bp) != 0)
+ goto error;
+ entp = ((struct ext2fs_htree_node *)bp->b_data)->h_entries;
+ info->h_levels_num++;
+ info->h_levels[info->h_levels_num - 1].h_bp = bp;
+ }
+
+error:
+ ext2fs_htree_release(info);
+ return -1;
+}
+
+/*
+ * Try to lookup a directory entry in HTree index
+ */
+int
+ext2fs_htree_lookup(struct inode *ip, const char *name, int namelen,
+ struct buf **bpp, int *entryoffp, doff_t *offp,
+ doff_t *prevoffp, doff_t *endusefulp, struct ext2fs_searchslot *ss)
+{
+ struct vnode *vp;
+ struct ext2fs_htree_lookup_info info;
+ struct ext2fs_htree_entry *leaf_node;
+ struct m_ext2fs *m_fs;
+ struct buf *bp;
+ uint32_t blk;
+ uint32_t dirhash;
+ uint32_t bsize;
+ uint8_t hash_version;
+ int search_next;
+
+ m_fs = ip->i_e2fs;
+ bsize = m_fs->e2fs_bsize;
+ vp = ITOV(ip);
+
+ /* TODO: print error msg because we don't lookup '.' and '..' */
+
+ memset(&info, 0, sizeof(info));
+ if (ext2fs_htree_find_leaf(ip, name, namelen, &dirhash,
+ &hash_version, &info)) {
+ return -1;
+ }
+
+ do {
+ leaf_node = info.h_levels[info.h_levels_num - 1].h_entry;
+ blk = ext2fs_htree_get_block(leaf_node);
+ if (ext2fs_blkatoff(vp, blk * bsize, NULL, &bp) != 0) {
+ ext2fs_htree_release(&info);
+ return -1;
+ }
+
+ *offp = blk * bsize;
+ *entryoffp = 0;
+ *prevoffp = blk * bsize;
+ *endusefulp = blk * bsize;
+
+ if (ss->slotstatus == NONE) {
+ ss->slotoffset = -1;
+ ss->slotfreespace = 0;
+ }
+
+ int found;
+ if (ext2fs_search_dirblock(ip, bp->b_data, &found,
+ name, namelen, entryoffp, offp, prevoffp,
+ endusefulp, ss) != 0) {
+ brelse(bp, 0);
+ ext2fs_htree_release(&info);
+ return -1;
+ }
+
+ if (found) {
+ *bpp = bp;
+ ext2fs_htree_release(&info);
+ return 0;
+ }
+
+ brelse(bp,0);
+ search_next = ext2fs_htree_check_next(ip, dirhash, name, &info);
+ } while (search_next);
+
+ ext2fs_htree_release(&info);
+ return ENOENT;
+}
Index: src/sys/ufs/ext2fs/ext2fs_htree.h
diff -u /dev/null src/sys/ufs/ext2fs/ext2fs_htree.h:1.1
--- /dev/null Fri Jun 24 13:21:30 2016
+++ src/sys/ufs/ext2fs/ext2fs_htree.h Fri Jun 24 13:21:30 2016
@@ -0,0 +1,101 @@
+/* $NetBSD: ext2fs_htree.h,v 1.1 2016/06/24 17:21:30 christos Exp $ */
+
+/*-
+ * Copyright (c) 2010, 2012 Zheng Liu <[email protected]>
+ * Copyright (c) 2012, Vyacheslav Matyushin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: head/sys/fs/ext2fs/htree.h 262623 2014-02-28 21:25:32Z pfg $
+ */
+#ifndef _FS_EXT2FS_HTREE_H_
+#define _FS_EXT2FS_HTREE_H_
+
+/* EXT3 HTree directory indexing */
+
+#define EXT2_HTREE_LEGACY 0
+#define EXT2_HTREE_HALF_MD4 1
+#define EXT2_HTREE_TEA 2
+#define EXT2_HTREE_LEGACY_UNSIGNED 3
+#define EXT2_HTREE_HALF_MD4_UNSIGNED 4
+#define EXT2_HTREE_TEA_UNSIGNED 5
+
+#define EXT2_HTREE_EOF 0x7FFFFFFF
+
+struct ext2fs_fake_direct {
+ uint32_t e2d_ino; /* inode number of entry */
+ uint16_t e2d_reclen; /* length of this record */
+ uint8_t e2d_namlen; /* length of string in d_name */
+ uint8_t e2d_type; /* file type */
+};
+
+struct ext2fs_htree_count {
+ uint16_t h_entries_max;
+ uint16_t h_entries_num;
+};
+
+struct ext2fs_htree_entry {
+ uint32_t h_hash;
+ uint32_t h_blk;
+};
+
+struct ext2fs_htree_root_info {
+ uint32_t h_reserved1;
+ uint8_t h_hash_version;
+ uint8_t h_info_len;
+ uint8_t h_ind_levels;
+ uint8_t h_reserved2;
+};
+
+struct ext2fs_htree_root {
+ struct ext2fs_fake_direct h_dot;
+ char h_dot_name[4];
+ struct ext2fs_fake_direct h_dotdot;
+ char h_dotdot_name[4];
+ struct ext2fs_htree_root_info h_info;
+ struct ext2fs_htree_entry h_entries[0];
+};
+
+struct ext2fs_htree_node {
+ struct ext2fs_fake_direct h_fake_dirent;
+ struct ext2fs_htree_entry h_entries[0];
+};
+
+struct ext2fs_htree_lookup_level {
+ struct buf *h_bp;
+ struct ext2fs_htree_entry *h_entries;
+ struct ext2fs_htree_entry *h_entry;
+};
+
+struct ext2fs_htree_lookup_info {
+ struct ext2fs_htree_lookup_level h_levels[2];
+ uint32_t h_levels_num;
+};
+
+struct ext2fs_htree_sort_entry {
+ uint16_t h_offset;
+ uint16_t h_size;
+ uint32_t h_hash;
+};
+
+#endif /* !_FS_EXT2FS_HTREE_H_ */