Index: head/sys/fs/ext2fs/ext2_hash.c =================================================================== --- head/sys/fs/ext2fs/ext2_hash.c (revision 281669) +++ head/sys/fs/ext2fs/ext2_hash.c (nonexistent) @@ -1,316 +0,0 @@ -/*- - * Copyright (c) 2010, 2013 Zheng Liu - * 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$ - */ - -/* - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* 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)))) - -/* - * 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)); \ -} - -/* - * 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 -ext2_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 -ext2_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; -} - -static uint32_t -ext2_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); -} - -static void -ext2_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); - } -} - -int -ext2_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) { - ext2_prep_hashbuf(name, len, data, 16, unsigned_char); - ext2_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 = ext2_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) { - ext2_prep_hashbuf(name, len, data, 32, unsigned_char); - ext2_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); -} Property changes on: head/sys/fs/ext2fs/ext2_hash.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/fs/ext2fs/ext2_htree.c =================================================================== --- head/sys/fs/ext2fs/ext2_htree.c (revision 281669) +++ head/sys/fs/ext2fs/ext2_htree.c (nonexistent) @@ -1,899 +0,0 @@ -/*- - * Copyright (c) 2010, 2012 Zheng Liu - * 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$ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -static void ext2_append_entry(char *block, uint32_t blksize, - struct ext2fs_direct_2 *last_entry, - struct ext2fs_direct_2 *new_entry); -static int ext2_htree_append_block(struct vnode *vp, char *data, - struct componentname *cnp, uint32_t blksize); -static int ext2_htree_check_next(struct inode *ip, uint32_t hash, - const char *name, struct ext2fs_htree_lookup_info *info); -static int ext2_htree_cmp_sort_entry(const void *e1, const void *e2); -static int ext2_htree_find_leaf(struct inode *ip, const char *name, - int namelen, uint32_t *hash, uint8_t *hash_version, - struct ext2fs_htree_lookup_info *info); -static uint32_t ext2_htree_get_block(struct ext2fs_htree_entry *ep); -static uint16_t ext2_htree_get_count(struct ext2fs_htree_entry *ep); -static uint32_t ext2_htree_get_hash(struct ext2fs_htree_entry *ep); -static uint16_t ext2_htree_get_limit(struct ext2fs_htree_entry *ep); -static void ext2_htree_insert_entry_to_level(struct ext2fs_htree_lookup_level *level, - uint32_t hash, uint32_t blk); -static void ext2_htree_insert_entry(struct ext2fs_htree_lookup_info *info, - uint32_t hash, uint32_t blk); -static uint32_t ext2_htree_node_limit(struct inode *ip); -static void ext2_htree_set_block(struct ext2fs_htree_entry *ep, - uint32_t blk); -static void ext2_htree_set_count(struct ext2fs_htree_entry *ep, - uint16_t cnt); -static void ext2_htree_set_hash(struct ext2fs_htree_entry *ep, - uint32_t hash); -static void ext2_htree_set_limit(struct ext2fs_htree_entry *ep, - uint16_t limit); -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); -static void ext2_htree_release(struct ext2fs_htree_lookup_info *info); -static uint32_t ext2_htree_root_limit(struct inode *ip, int len); -static int ext2_htree_writebuf(struct ext2fs_htree_lookup_info *info); - -int -ext2_htree_has_idx(struct inode *ip) -{ - if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) && - ip->i_flag & IN_E4INDEX) - return (1); - else - return (0); -} - -static int -ext2_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; - - do { - level = &info->h_levels[idx]; - level->h_entry++; - if (level->h_entry < level->h_entries + - ext2_htree_get_count(level->h_entries)) - break; - if (idx == 0) - return (0); - idx--; - levels++; - } while (1); - - next_hash = ext2_htree_get_hash(level->h_entry); - if ((hash & 1) == 0) { - if (hash != (next_hash & ~1)) - return (0); - } - - while (levels > 0) { - levels--; - if (ext2_blkatoff(vp, ext2_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); - level->h_bp = bp; - level->h_entry = level->h_entries = - ((struct ext2fs_htree_node *)bp->b_data)->h_entries; - } - - return (1); -} - -static uint32_t -ext2_htree_get_block(struct ext2fs_htree_entry *ep) -{ - return (ep->h_blk & 0x00FFFFFF); -} - -static void -ext2_htree_set_block(struct ext2fs_htree_entry *ep, uint32_t blk) -{ - ep->h_blk = blk; -} - -static uint16_t -ext2_htree_get_count(struct ext2fs_htree_entry *ep) -{ - return (((struct ext2fs_htree_count *)(ep))->h_entries_num); -} - -static void -ext2_htree_set_count(struct ext2fs_htree_entry *ep, uint16_t cnt) -{ - ((struct ext2fs_htree_count *)(ep))->h_entries_num = cnt; -} - -static uint32_t -ext2_htree_get_hash(struct ext2fs_htree_entry *ep) -{ - return (ep->h_hash); -} - -static uint16_t -ext2_htree_get_limit(struct ext2fs_htree_entry *ep) -{ - return (((struct ext2fs_htree_count *)(ep))->h_entries_max); -} - -static void -ext2_htree_set_hash(struct ext2fs_htree_entry *ep, uint32_t hash) -{ - ep->h_hash = hash; -} - -static void -ext2_htree_set_limit(struct ext2fs_htree_entry *ep, uint16_t limit) -{ - ((struct ext2fs_htree_count *)(ep))->h_entries_max = limit; -} - -static void -ext2_htree_release(struct ext2fs_htree_lookup_info *info) -{ - int i; - - for (i = 0; i < info->h_levels_num; i++) { - struct buf *bp = info->h_levels[i].h_bp; - if (bp != NULL) - brelse(bp); - } -} - -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)); -} - -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)); -} - -static int -ext2_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; - 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 (ext2_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; - - ext2_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 (ext2_htree_get_limit(entp) != - ext2_htree_root_limit(ip, rootp->h_info.h_info_len)) - goto error; - - while (1) { - cnt = ext2_htree_get_count(entp); - if (cnt == 0 || cnt > ext2_htree_get_limit(entp)) - goto error; - - start = entp + 1; - end = entp + cnt - 1; - while (start <= end) { - middle = start + (end - start) / 2; - if (ext2_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 (ext2_blkatoff(vp, - ext2_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: - ext2_htree_release(info); - return (-1); -} - -/* - * Try to lookup a directory entry in HTree index - */ -int -ext2_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; - int found = 0; - - 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 (ext2_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 = ext2_htree_get_block(leaf_node); - if (ext2_blkatoff(vp, blk * bsize, NULL, &bp) != 0) { - ext2_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; - } - - if (ext2_search_dirblock(ip, bp->b_data, &found, - name, namelen, entryoffp, offp, prevoffp, - endusefulp, ss) != 0) { - brelse(bp); - ext2_htree_release(&info); - return (-1); - } - - if (found) { - *bpp = bp; - ext2_htree_release(&info); - return (0); - } - - brelse(bp); - search_next = ext2_htree_check_next(ip, dirhash, name, &info); - } while (search_next); - - ext2_htree_release(&info); - return (ENOENT); -} - -static int -ext2_htree_append_block(struct vnode *vp, char *data, - struct componentname *cnp, uint32_t blksize) -{ - struct iovec aiov; - struct uio auio; - struct inode *dp = VTOI(vp); - uint64_t cursize, newsize; - int error; - - cursize = roundup(dp->i_size, blksize); - newsize = cursize + blksize; - - auio.uio_offset = cursize; - auio.uio_resid = blksize; - aiov.iov_len = blksize; - aiov.iov_base = data; - auio.uio_iov = &aiov; - auio.uio_iovcnt = 1; - auio.uio_rw = UIO_WRITE; - auio.uio_segflg = UIO_SYSSPACE; - error = VOP_WRITE(vp, &auio, IO_SYNC, cnp->cn_cred); - if (!error) - dp->i_size = newsize; - - return (error); -} - -static int -ext2_htree_writebuf(struct ext2fs_htree_lookup_info *info) -{ - int i, error; - - for (i = 0; i < info->h_levels_num; i++) { - struct buf *bp = info->h_levels[i].h_bp; - error = bwrite(bp); - if (error) - return (error); - } - - return (0); -} - -static void -ext2_htree_insert_entry_to_level(struct ext2fs_htree_lookup_level *level, - uint32_t hash, uint32_t blk) -{ - struct ext2fs_htree_entry *target; - int entries_num; - - target = level->h_entry + 1; - entries_num = ext2_htree_get_count(level->h_entries); - - memmove(target + 1, target, (char *)(level->h_entries + entries_num) - - (char *)target); - ext2_htree_set_block(target, blk); - ext2_htree_set_hash(target, hash); - ext2_htree_set_count(level->h_entries, entries_num + 1); -} - -/* - * Insert an index entry to the index node. - */ -static void -ext2_htree_insert_entry(struct ext2fs_htree_lookup_info *info, - uint32_t hash, uint32_t blk) -{ - struct ext2fs_htree_lookup_level *level; - - level = &info->h_levels[info->h_levels_num - 1]; - ext2_htree_insert_entry_to_level(level, hash, blk); -} - -/* - * Compare two entry sort descriptors by name hash value. - * This is used together with qsort. - */ -static int -ext2_htree_cmp_sort_entry(const void *e1, const void *e2) -{ - const struct ext2fs_htree_sort_entry *entry1, *entry2; - - entry1 = (const struct ext2fs_htree_sort_entry *)e1; - entry2 = (const struct ext2fs_htree_sort_entry *)e2; - - if (entry1->h_hash < entry2->h_hash) - return (-1); - if (entry1->h_hash > entry2->h_hash) - return (1); - return (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)); -} - -/* - * 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); -} - -/* - * 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); -} - -/* - * Add an entry to the directory using htree index. - */ -int -ext2_htree_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry, - struct componentname *cnp) -{ - struct ext2fs_htree_entry *entries, *leaf_node; - struct ext2fs_htree_lookup_info info; - struct buf *bp = NULL; - struct ext2fs *fs; - struct m_ext2fs *m_fs; - struct inode *ip; - uint16_t ent_num; - uint32_t dirhash, split_hash; - uint32_t blksize, blknum; - uint64_t cursize, dirsize; - uint8_t hash_version; - char *newdirblock = NULL; - char *newidxblock = NULL; - struct ext2fs_htree_node *dst_node; - struct ext2fs_htree_entry *dst_entries; - struct ext2fs_htree_entry *root_entires; - struct buf *dst_bp = NULL; - int error, write_bp = 0, write_dst_bp = 0, write_info = 0; - - ip = VTOI(dvp); - m_fs = ip->i_e2fs; - fs = m_fs->e2fs; - blksize = m_fs->e2fs_bsize; - - if (ip->i_count != 0) - return ext2_add_entry(dvp, entry); - - /* Target directory block is full, split it */ - memset(&info, 0, sizeof(info)); - error = ext2_htree_find_leaf(ip, entry->e2d_name, entry->e2d_namlen, - &dirhash, &hash_version, &info); - if (error) - return (error); - - entries = info.h_levels[info.h_levels_num - 1].h_entries; - ent_num = ext2_htree_get_count(entries); - if (ent_num == ext2_htree_get_limit(entries)) { - /* Split the index node. */ - root_entires = info.h_levels[0].h_entries; - newidxblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO); - dst_node = (struct ext2fs_htree_node *)newidxblock; - dst_entries = dst_node->h_entries; - memset(&dst_node->h_fake_dirent, 0, - sizeof(dst_node->h_fake_dirent)); - dst_node->h_fake_dirent.e2d_reclen = blksize; - - cursize = roundup(ip->i_size, blksize); - dirsize = cursize + blksize; - blknum = dirsize / blksize - 1; - - error = ext2_htree_append_block(dvp, newidxblock, - cnp, blksize); - if (error) - goto finish; - error = ext2_blkatoff(dvp, cursize, NULL, &dst_bp); - if (error) - goto finish; - dst_node = (struct ext2fs_htree_node *)dst_bp->b_data; - dst_entries = dst_node->h_entries; - - if (info.h_levels_num == 2) { - uint16_t src_ent_num, dst_ent_num; - - if (ext2_htree_get_count(root_entires) == - ext2_htree_get_limit(root_entires)) { - /* Directory index is full */ - error = EIO; - goto finish; - } - - src_ent_num = ent_num / 2; - dst_ent_num = ent_num - src_ent_num; - split_hash = ext2_htree_get_hash(entries + src_ent_num); - - /* Move half of index entries to the new index node */ - memcpy(dst_entries, entries + src_ent_num, - dst_ent_num * sizeof(struct ext2fs_htree_entry)); - ext2_htree_set_count(entries, src_ent_num); - ext2_htree_set_count(dst_entries, dst_ent_num); - ext2_htree_set_limit(dst_entries, - ext2_htree_node_limit(ip)); - - if (info.h_levels[1].h_entry >= entries + src_ent_num) { - struct buf *tmp = info.h_levels[1].h_bp; - info.h_levels[1].h_bp = dst_bp; - dst_bp = tmp; - - info.h_levels[1].h_entry = - info.h_levels[1].h_entry - - (entries + src_ent_num) + - dst_entries; - info.h_levels[1].h_entries = dst_entries; - } - ext2_htree_insert_entry_to_level(&info.h_levels[0], - split_hash, blknum); - - /* Write new index node to disk */ - error = bwrite(dst_bp); - ip->i_flag |= IN_CHANGE | IN_UPDATE; - if (error) - goto finish; - write_dst_bp = 1; - } else { - /* Create second level for htree index */ - struct ext2fs_htree_root *idx_root; - - memcpy(dst_entries, entries, - ent_num * sizeof(struct ext2fs_htree_entry)); - ext2_htree_set_limit(dst_entries, - ext2_htree_node_limit(ip)); - - idx_root = (struct ext2fs_htree_root *) - info.h_levels[0].h_bp->b_data; - idx_root->h_info.h_ind_levels = 1; - - ext2_htree_set_count(entries, 1); - ext2_htree_set_block(entries, blknum); - - info.h_levels_num = 2; - info.h_levels[1].h_entries = dst_entries; - info.h_levels[1].h_entry = info.h_levels[0].h_entry - - info.h_levels[0].h_entries + dst_entries; - info.h_levels[1].h_bp = dst_bp; - } - } - - leaf_node = info.h_levels[info.h_levels_num - 1].h_entry; - blknum = ext2_htree_get_block(leaf_node); - error = ext2_blkatoff(dvp, blknum * blksize, NULL, &bp); - if (error) - goto finish; - - /* Split target directory block */ - newdirblock = malloc(blksize, M_TEMP, M_WAITOK | M_ZERO); - ext2_htree_split_dirblock((char *)bp->b_data, newdirblock, blksize, - fs->e3fs_hash_seed, hash_version, &split_hash, entry); - cursize = roundup(ip->i_size, blksize); - dirsize = cursize + blksize; - blknum = dirsize / blksize - 1; - - /* Add index entry for the new directory block */ - ext2_htree_insert_entry(&info, split_hash, blknum); - - /* Write the new directory block to the end of the directory */ - error = ext2_htree_append_block(dvp, newdirblock, cnp, blksize); - if (error) - goto finish; - - /* Write the target directory block */ - error = bwrite(bp); - ip->i_flag |= IN_CHANGE | IN_UPDATE; - if (error) - goto finish; - write_bp = 1; - - /* Write the index block */ - error = ext2_htree_writebuf(&info); - if (!error) - write_info = 1; - -finish: - if (dst_bp != NULL && !write_dst_bp) - brelse(dst_bp); - if (bp != NULL && !write_bp) - brelse(bp); - if (newdirblock != NULL) - free(newdirblock, M_TEMP); - if (newidxblock != NULL) - free(newidxblock, M_TEMP); - if (!write_info) - ext2_htree_release(&info); - return (error); -} Property changes on: head/sys/fs/ext2fs/ext2_htree.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sys/fs/ext2fs/ext2_dir.h =================================================================== --- head/sys/fs/ext2fs/ext2_dir.h (revision 281669) +++ head/sys/fs/ext2fs/ext2_dir.h (revision 281670) @@ -1,101 +1,86 @@ /*- * Copyright (c) 2009 Aditya Sarawgi * 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 AUTHOR 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 AUTHOR 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$ */ #ifndef _FS_EXT2FS_EXT2_DIR_H_ #define _FS_EXT2FS_EXT2_DIR_H_ /* * Structure of a directory entry */ #define EXT2FS_MAXNAMLEN 255 struct ext2fs_direct { uint32_t e2d_ino; /* inode number of entry */ uint16_t e2d_reclen; /* length of this record */ uint16_t e2d_namlen; /* length of string in e2d_name */ char e2d_name[EXT2FS_MAXNAMLEN];/* name with length<=EXT2FS_MAXNAMLEN */ }; - -enum slotstatus { - NONE, - COMPACT, - FOUND -}; - -struct ext2fs_searchslot { - enum 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 */ -}; - /* * The new version of the directory entry. Since EXT2 structures are * stored in intel byte order, and the name_len field could never be * bigger than 255 chars, it's safe to reclaim the extra byte for the * file_type field. */ struct ext2fs_direct_2 { uint32_t e2d_ino; /* inode number of entry */ uint16_t e2d_reclen; /* length of this record */ uint8_t e2d_namlen; /* length of string in e2d_name */ uint8_t e2d_type; /* file type */ char e2d_name[EXT2FS_MAXNAMLEN];/* name with length<=EXT2FS_MAXNAMLEN */ }; /* * Maximal count of links to a file */ #define EXT2_LINK_MAX 32000 /* * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ #define EXT2_FT_UNKNOWN 0 #define EXT2_FT_REG_FILE 1 #define EXT2_FT_DIR 2 #define EXT2_FT_CHRDEV 3 #define EXT2_FT_BLKDEV 4 #define EXT2_FT_FIFO 5 #define EXT2_FT_SOCK 6 #define EXT2_FT_SYMLINK 7 #define EXT2_FT_MAX 8 /* * 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(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) #endif /* !_FS_EXT2FS_EXT2_DIR_H_ */ Index: head/sys/fs/ext2fs/ext2_extern.h =================================================================== --- head/sys/fs/ext2fs/ext2_extern.h (revision 281669) +++ head/sys/fs/ext2fs/ext2_extern.h (revision 281670) @@ -1,113 +1,98 @@ /*- * modified for EXT2FS support in Lites 1.1 * * Aug 1995, Godmar Back (gback@cs.utah.edu) * University of Utah, Department of Computer Science */ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * * @(#)ffs_extern.h 8.3 (Berkeley) 4/16/94 * $FreeBSD$ */ #ifndef _FS_EXT2FS_EXT2_EXTERN_H_ #define _FS_EXT2FS_EXT2_EXTERN_H_ struct ext2fs_dinode; -struct ext2fs_direct_2; -struct ext2fs_searchslot; struct indir; struct inode; struct mount; struct vfsconf; struct vnode; -int ext2_add_entry(struct vnode *, struct ext2fs_direct_2 *); int ext2_alloc(struct inode *, daddr_t, e4fs_daddr_t, int, struct ucred *, e4fs_daddr_t *); int ext2_balloc(struct inode *, e2fs_lbn_t, int, struct ucred *, struct buf **, int); int ext2_blkatoff(struct vnode *, off_t, char **, struct buf **); void ext2_blkfree(struct inode *, e4fs_daddr_t, long); e4fs_daddr_t ext2_blkpref(struct inode *, e2fs_lbn_t, int, e2fs_daddr_t *, e2fs_daddr_t); int ext2_bmap(struct vop_bmap_args *); int ext2_bmaparray(struct vnode *, daddr_t, daddr_t *, int *, int *); void ext2_clusteracct(struct m_ext2fs *, char *, int, daddr_t, int); void ext2_dirbad(struct inode *ip, doff_t offset, char *how); void ext2_ei2i(struct ext2fs_dinode *, struct inode *); int ext2_getlbns(struct vnode *, daddr_t, struct indir *, int *); void ext2_i2ei(struct inode *, struct ext2fs_dinode *); void ext2_itimes(struct vnode *vp); int ext2_reallocblks(struct vop_reallocblks_args *); int ext2_reclaim(struct vop_reclaim_args *); int ext2_truncate(struct vnode *, off_t, int, struct ucred *, struct thread *); int ext2_update(struct vnode *, int); int ext2_valloc(struct vnode *, int, struct ucred *, struct vnode **); int ext2_vfree(struct vnode *, ino_t, int); int ext2_vinit(struct mount *, struct vop_vector *, struct vnode **vpp); int ext2_lookup(struct vop_cachedlookup_args *); int ext2_readdir(struct vop_readdir_args *); #ifdef EXT2FS_DEBUG void ext2_print_inode(struct inode *); #endif int ext2_direnter(struct inode *, struct vnode *, struct componentname *); int ext2_dirremove(struct vnode *, struct componentname *); int ext2_dirrewrite(struct inode *, struct inode *, struct componentname *); int ext2_dirempty(struct inode *, ino_t, struct ucred *); int ext2_checkpath(struct inode *, struct inode *, struct ucred *); int cg_has_sb(int i); int ext2_inactive(struct vop_inactive_args *); -int ext2_htree_add_entry(struct vnode *, struct ext2fs_direct_2 *, - struct componentname *); -int ext2_htree_create_index(struct vnode *, struct componentname *, - struct ext2fs_direct_2 *); -int ext2_htree_has_idx(struct inode *); -int ext2_htree_hash(const char *, int, uint32_t *, int, uint32_t *, - uint32_t *); -int ext2_htree_lookup(struct inode *, const char *, int, struct buf **, - int *, doff_t *, doff_t *, doff_t *, struct ext2fs_searchslot *); -int ext2_search_dirblock(struct inode *, void *, int *, const char *, int, - int *, doff_t *, doff_t *, doff_t *, struct ext2fs_searchslot *); - /* Flags to low-level allocation routines. * The low 16-bits are reserved for IO_ flags from vnode.h. */ #define BA_CLRBUF 0x00010000 /* Clear invalid areas of buffer. */ #define BA_SEQMASK 0x7F000000 /* Bits holding seq heuristic. */ #define BA_SEQSHIFT 24 #define BA_SEQMAX 0x7F extern struct vop_vector ext2_vnodeops; extern struct vop_vector ext2_fifoops; #endif /* !_FS_EXT2FS_EXT2_EXTERN_H_ */ Index: head/sys/fs/ext2fs/ext2_lookup.c =================================================================== --- head/sys/fs/ext2fs/ext2_lookup.c (revision 281669) +++ head/sys/fs/ext2fs/ext2_lookup.c (revision 281670) @@ -1,1236 +1,1126 @@ /*- * modified for Lites 1.1 * * Aug 1995, Godmar Back (gback@cs.utah.edu) * University of Utah, Department of Computer Science */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INVARIANTS static int dirchk = 1; #else static int dirchk = 0; #endif static SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem"); SYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, ""); /* DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512) while it is the native blocksize in ext2fs - thus, a #define is no longer appropriate */ #undef DIRBLKSIZ static u_char ext2_ft_to_dt[] = { DT_UNKNOWN, /* EXT2_FT_UNKNOWN */ DT_REG, /* EXT2_FT_REG_FILE */ DT_DIR, /* EXT2_FT_DIR */ DT_CHR, /* EXT2_FT_CHRDEV */ DT_BLK, /* EXT2_FT_BLKDEV */ DT_FIFO, /* EXT2_FT_FIFO */ DT_SOCK, /* EXT2_FT_SOCK */ DT_LNK, /* EXT2_FT_SYMLINK */ }; #define FTTODT(ft) \ ((ft) < nitems(ext2_ft_to_dt) ? ext2_ft_to_dt[(ft)] : DT_UNKNOWN) static u_char dt_to_ext2_ft[] = { EXT2_FT_UNKNOWN, /* DT_UNKNOWN */ EXT2_FT_FIFO, /* DT_FIFO */ EXT2_FT_CHRDEV, /* DT_CHR */ EXT2_FT_UNKNOWN, /* unused */ EXT2_FT_DIR, /* DT_DIR */ EXT2_FT_UNKNOWN, /* unused */ EXT2_FT_BLKDEV, /* DT_BLK */ EXT2_FT_UNKNOWN, /* unused */ EXT2_FT_REG_FILE, /* DT_REG */ EXT2_FT_UNKNOWN, /* unused */ EXT2_FT_SYMLINK, /* DT_LNK */ EXT2_FT_UNKNOWN, /* unused */ EXT2_FT_SOCK, /* DT_SOCK */ EXT2_FT_UNKNOWN, /* unused */ EXT2_FT_UNKNOWN, /* DT_WHT */ }; #define DTTOFT(dt) \ ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN) static int ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de, int entryoffsetinblock); -static int ext2_is_dot_entry(struct componentname *cnp); static int ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp, ino_t *dd_ino); -static int -ext2_is_dot_entry(struct componentname *cnp) -{ - if (cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' && - (cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '0')) - return (1); - return (0); -} - /* * Vnode op for reading directories. */ int ext2_readdir(struct vop_readdir_args *ap) { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct buf *bp; struct inode *ip; struct ext2fs_direct_2 *dp, *edp; u_long *cookies; struct dirent dstdp; off_t offset, startoffset; size_t readcnt, skipcnt; ssize_t startresid; int ncookies; int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize; int error; if (uio->uio_offset < 0) return (EINVAL); ip = VTOI(vp); if (ap->a_ncookies != NULL) { ncookies = uio->uio_resid; if (uio->uio_offset >= ip->i_size) ncookies = 0; else if (ip->i_size - uio->uio_offset < ncookies) ncookies = ip->i_size - uio->uio_offset; ncookies = ncookies / (offsetof(struct ext2fs_direct_2, e2d_namlen) + 4) + 1; cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK); *ap->a_ncookies = ncookies; *ap->a_cookies = cookies; } else { ncookies = 0; cookies = NULL; } offset = startoffset = uio->uio_offset; startresid = uio->uio_resid; error = 0; while (error == 0 && uio->uio_resid > 0 && uio->uio_offset < ip->i_size) { error = ext2_blkatoff(vp, uio->uio_offset, NULL, &bp); if (error) break; if (bp->b_offset + bp->b_bcount > ip->i_size) readcnt = ip->i_size - bp->b_offset; else readcnt = bp->b_bcount; skipcnt = (size_t)(uio->uio_offset - bp->b_offset) & ~(size_t)(DIRBLKSIZ - 1); offset = bp->b_offset + skipcnt; dp = (struct ext2fs_direct_2 *)&bp->b_data[skipcnt]; edp = (struct ext2fs_direct_2 *)&bp->b_data[readcnt]; while (error == 0 && uio->uio_resid > 0 && dp < edp) { if (dp->e2d_reclen <= offsetof(struct ext2fs_direct_2, e2d_namlen) || (caddr_t)dp + dp->e2d_reclen > (caddr_t)edp) { error = EIO; break; } /*- * "New" ext2fs directory entries differ in 3 ways * from ufs on-disk ones: * - the name is not necessarily NUL-terminated. * - the file type field always exists and always * follows the name length field. * - the file type is encoded in a different way. * * "Old" ext2fs directory entries need no special * conversions, since they are binary compatible * with "new" entries having a file type of 0 (i.e., * EXT2_FT_UNKNOWN). Splitting the old name length * field didn't make a mess like it did in ufs, * because ext2fs uses a machine-independent disk * layout. */ dstdp.d_namlen = dp->e2d_namlen; dstdp.d_type = FTTODT(dp->e2d_type); if (offsetof(struct ext2fs_direct_2, e2d_namlen) + dstdp.d_namlen > dp->e2d_reclen) { error = EIO; break; } if (offset < startoffset || dp->e2d_ino == 0) goto nextentry; dstdp.d_fileno = dp->e2d_ino; dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp); bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen); dstdp.d_name[dstdp.d_namlen] = '\0'; if (dstdp.d_reclen > uio->uio_resid) { if (uio->uio_resid == startresid) error = EINVAL; else error = EJUSTRETURN; break; } /* Advance dp. */ error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio); if (error) break; if (cookies != NULL) { KASSERT(ncookies > 0, ("ext2_readdir: cookies buffer too small")); *cookies = offset + dp->e2d_reclen; cookies++; ncookies--; } nextentry: offset += dp->e2d_reclen; dp = (struct ext2fs_direct_2 *)((caddr_t)dp + dp->e2d_reclen); } bqrelse(bp); uio->uio_offset = offset; } /* We need to correct uio_offset. */ uio->uio_offset = offset; if (error == EJUSTRETURN) error = 0; if (ap->a_ncookies != NULL) { if (error == 0) { ap->a_ncookies -= ncookies; } else { free(*ap->a_cookies, M_TEMP); *ap->a_ncookies = 0; *ap->a_cookies = NULL; } } if (error == 0 && ap->a_eofflag) *ap->a_eofflag = ip->i_size <= uio->uio_offset; return (error); } /* * 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 ext2_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 */ int ext2_lookup(struct vop_cachedlookup_args *ap) { return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL)); } static int ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp, ino_t *dd_ino) { struct inode *dp; /* inode for directory being searched */ struct buf *bp; /* a buffer of directory entries */ struct ext2fs_direct_2 *ep; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ - struct ext2fs_searchslot ss; + enum {NONE, COMPACT, FOUND} slotstatus; + doff_t slotoffset; /* offset of area with free space */ doff_t i_diroff; /* cached i_diroff value */ doff_t i_offset; /* cached i_offset value */ + 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 error; + int namlen, error; struct ucred *cred = cnp->cn_cred; int flags = cnp->cn_flags; int nameiop = cnp->cn_nameiop; ino_t ino, ino1; int ltype; - int entry_found = 0; int DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize; if (vpp != NULL) *vpp = NULL; dp = VTOI(vdp); bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; restart: bp = NULL; - ss.slotoffset = -1; + slotoffset = -1; /* * 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. */ i_diroff = dp->i_diroff; - ss.slotstatus = FOUND; - ss.slotfreespace = ss.slotsize = ss.slotneeded = 0; + slotstatus = FOUND; + slotfreespace = slotsize = slotneeded = 0; if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { - ss.slotstatus = NONE; - ss.slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); + slotstatus = NONE; + slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen); /* was - ss.slotneeded = (sizeof(struct direct) - MAXNAMLEN + + slotneeded = (sizeof(struct direct) - MAXNAMLEN + cnp->cn_namelen + 3) &~ 3; */ } /* - * 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 (!ext2_is_dot_entry(cnp) && ext2_htree_has_idx(dp)) { - numdirpasses = 1; - entryoffsetinblock = 0; - switch (ext2_htree_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen, - &bp, &entryoffsetinblock, &i_offset, &prevoff, - &enduseful, &ss)) { - case 0: - ep = (struct ext2fs_direct_2 *)((char *)bp->b_data + - (i_offset & bmask)); - goto foundentry; - case ENOENT: - i_offset = roundup2(dp->i_size, DIRBLKSIZ); - goto notfound; - default: - /* - * Something failed; just fallback to do a linear - * search. - */ - break; - } - } - - /* * 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. */ if (nameiop != LOOKUP || i_diroff == 0 || i_diroff > dp->i_size) { entryoffsetinblock = 0; i_offset = 0; numdirpasses = 1; } else { i_offset = i_diroff; if ((entryoffsetinblock = i_offset & bmask) && (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, &bp))) return (error); numdirpasses = 2; nchstats.ncs_2passes++; } prevoff = i_offset; endsearch = roundup2(dp->i_size, DIRBLKSIZ); enduseful = 0; searchloop: while (i_offset < endsearch) { /* * If necessary, get the next directory block. */ - if (bp != NULL) - brelse(bp); - error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, &bp); - if (error != 0) - return (error); - entryoffsetinblock = 0; + if ((i_offset & bmask) == 0) { + if (bp != NULL) + brelse(bp); + if ((error = + ext2_blkatoff(vdp, (off_t)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 (ss.slotstatus == NONE && + if (slotstatus == NONE && (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) { - ss.slotoffset = -1; - ss.slotfreespace = 0; + slotoffset = -1; + slotfreespace = 0; } - error = ext2_search_dirblock(dp, bp->b_data, &entry_found, - cnp->cn_nameptr, cnp->cn_namelen, - &entryoffsetinblock, &i_offset, &prevoff, - &enduseful, &ss); - if (error != 0) { - brelse(bp); - return (error); + /* + * 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 setting + * "vfs.e2fs.dirchk" to be true. + */ + ep = (struct ext2fs_direct_2 *) + ((char *)bp->b_data + entryoffsetinblock); + if (ep->e2d_reclen == 0 || + (dirchk && ext2_dirbadentry(vdp, ep, entryoffsetinblock))) { + int i; + ext2_dirbad(dp, i_offset, "mangled entry"); + i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)); + i_offset += i; + entryoffsetinblock += i; + continue; } - if (entry_found) { - ep = (struct ext2fs_direct_2 *)((char *)bp->b_data + - (entryoffsetinblock & bmask)); -foundentry: - ino = ep->e2d_ino; - goto found; + + /* + * 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->e2d_reclen; + + if (ep->e2d_ino != 0) + size -= EXT2_DIR_REC_LEN(ep->e2d_namlen); + if (size > 0) { + if (size >= slotneeded) { + slotstatus = FOUND; + slotoffset = i_offset; + slotsize = ep->e2d_reclen; + } else if (slotstatus == NONE) { + slotfreespace += size; + if (slotoffset == -1) + slotoffset = i_offset; + if (slotfreespace >= slotneeded) { + slotstatus = COMPACT; + slotsize = i_offset + + ep->e2d_reclen - slotoffset; + } + } + } } + + /* + * Check for a name match. + */ + if (ep->e2d_ino) { + namlen = ep->e2d_namlen; + if (namlen == cnp->cn_namelen && + !bcmp(cnp->cn_nameptr, ep->e2d_name, + (unsigned)namlen)) { + /* + * Save directory entry's inode number and + * reclen in ndp->ni_ufs area, and release + * directory buffer. + */ + ino = ep->e2d_ino; + goto found; + } + } + prevoff = i_offset; + i_offset += ep->e2d_reclen; + entryoffsetinblock += ep->e2d_reclen; + if (ep->e2d_ino) + enduseful = i_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. */ if (numdirpasses == 2) { numdirpasses--; i_offset = 0; endsearch = 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 == CREATE || nameiop == RENAME) && (flags & ISLASTCN) && dp->i_nlink != 0) { /* * Access for write is interpreted as allowing * creation of files in the directory. */ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 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 (ss.slotstatus == NONE) { + if (slotstatus == NONE) { dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ); dp->i_count = 0; enduseful = dp->i_offset; } else { - dp->i_offset = ss.slotoffset; - dp->i_count = ss.slotsize; - if (enduseful < ss.slotoffset + ss.slotsize) - enduseful = ss.slotoffset + ss.slotsize; + dp->i_offset = slotoffset; + dp->i_count = slotsize; + if (enduseful < slotoffset + slotsize) + enduseful = slotoffset + slotsize; } dp->i_endoff = roundup2(enduseful, DIRBLKSIZ); /* * 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. */ cnp->cn_flags |= SAVENAME; return (EJUSTRETURN); } /* * Insert name into cache (as non-existent) if appropriate. */ if ((cnp->cn_flags & MAKEENTRY) != 0) cache_enter(vdp, NULL, cnp); return (ENOENT); found: if (dd_ino != NULL) *dd_ino = ino; if (numdirpasses == 2) nchstats.ncs_pass2++; /* * Check that directory length properly reflects presence * of this entry. */ if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen) > dp->i_size) { ext2_dirbad(dp, i_offset, "i_size too small"); dp->i_size = entryoffsetinblock+EXT2_DIR_REC_LEN(ep->e2d_namlen); 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 ((flags & ISLASTCN) && nameiop == LOOKUP) dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1); /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. */ if (nameiop == DELETE && (flags & ISLASTCN)) { if (flags & LOCKPARENT) ASSERT_VOP_ELOCKED(vdp, __FUNCTION__); /* * Write access to directory required to delete files. */ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 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(). * * Technically we shouldn't be setting these in the * WANTPARENT case (first lookup in rename()), but any * lookups that will result in directory changes will * overwrite these. */ dp->i_offset = i_offset; if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0) dp->i_count = 0; else dp->i_count = dp->i_offset - prevoff; if (dd_ino != NULL) return (0); if (dp->i_number == ino) { VREF(vdp); *vpp = vdp; return (0); } if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, &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; 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 == RENAME && (flags & ISLASTCN)) { if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ dp->i_offset = i_offset; if (dp->i_number == ino) return (EISDIR); if (dd_ino != NULL) return (0); if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, &tdp)) != 0) return (error); *vpp = tdp; cnp->cn_flags |= SAVENAME; return (0); } if (dd_ino != NULL) 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 & ISDOTDOT) { error = vn_vget_ino(pdp, ino, cnp->cn_lkflags, &tdp); if (pdp->v_iflag & VI_DOOMED) { if (error == 0) vput(tdp); error = ENOENT; } if (error) return (error); /* * Recheck that ".." entry in the vdp directory points * to the inode we looked up before vdp lock was * dropped. */ error = ext2_lookup_ino(pdp, NULL, cnp, &ino1); if (error) { vput(tdp); return (error); } if (ino1 != ino) { vput(tdp); goto restart; } *vpp = tdp; } else if (dp->i_number == ino) { VREF(vdp); /* we want ourself, ie "." */ /* * When we lookup "." we still can be asked to lock it * differently. */ ltype = cnp->cn_lkflags & LK_TYPE_MASK; if (ltype != VOP_ISLOCKED(vdp)) { if (ltype == LK_EXCLUSIVE) vn_lock(vdp, LK_UPGRADE | LK_RETRY); else /* if (ltype == LK_SHARED) */ vn_lock(vdp, LK_DOWNGRADE | LK_RETRY); } *vpp = vdp; } else { if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp)) != 0) return (error); *vpp = tdp; } /* * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); return (0); } -int -ext2_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; - struct ext2fs_direct_2 *ep, *top; - uint32_t bsize = ip->i_e2fs->e2fs_bsize; - int offset = *entryoffsetinblockp; - int namlen; - - vdp = ITOV(ip); - - ep = (struct ext2fs_direct_2 *)((char *)data + offset); - top = (struct ext2fs_direct_2 *)((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 && ext2_dirbadentry(vdp, ep, offset))) { - int i; - ext2_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 > 0) { - if (size >= ssp->slotneeded) { - ssp->slotstatus = FOUND; - ssp->slotoffset = *offp; - ssp->slotsize = ep->e2d_reclen; - } else if (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 && - !bcmp(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 = (struct ext2fs_direct_2 *)((char *)data + offset); - } - - return (0); -} - void ext2_dirbad(struct inode *ip, doff_t offset, char *how) { struct mount *mp; mp = ITOV(ip)->v_mount; if ((mp->mnt_flag & MNT_RDONLY) == 0) panic("ext2_dirbad: %s: bad dir ino %ju at offset %ld: %s\n", mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number, (long)offset, how); else (void)printf("%s: bad dir ino %ju at offset %ld: %s\n", mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number, (long)offset, how); } /* * Do consistency checking on a directory entry: * record length must be multiple of 4 * entry must fit in rest of its DIRBLKSIZ block * record must be large enough to contain entry * name is not longer than MAXNAMLEN * name must be as long as advertised, and null terminated */ /* * changed so that it confirms to ext2_check_dir_entry */ static int ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de, int entryoffsetinblock) { int DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize; char * error_msg = NULL; if (de->e2d_reclen < EXT2_DIR_REC_LEN(1)) error_msg = "rec_len is smaller than minimal"; else if (de->e2d_reclen % 4 != 0) error_msg = "rec_len % 4 != 0"; else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen)) error_msg = "reclen is too small for name_len"; else if (entryoffsetinblock + de->e2d_reclen > 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) { printf("bad directory entry: %s\n", error_msg); printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n", entryoffsetinblock, (unsigned long)de->e2d_ino, de->e2d_reclen, de->e2d_namlen); } return error_msg == NULL ? 0 : 1; } /* * Write a directory entry after a call to namei, using the parameters * that it left in nameidata. 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 ext2fs_direct_2 *ep, *nep; struct inode *dp; + struct buf *bp; struct ext2fs_direct_2 newdir; struct iovec aiov; struct uio auio; - int error, newentrysize; - int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize; + u_int dsize; + int error, loc, newentrysize, spacefree; + char *dirbuf; + int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize; #ifdef INVARIANTS if ((cnp->cn_flags & SAVENAME) == 0) panic("ext2_direnter: missing name"); #endif dp = VTOI(dvp); newdir.e2d_ino = ip->i_number; newdir.e2d_namlen = cnp->cn_namelen; if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_FTYPE)) newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode)); else newdir.e2d_type = EXT2_FT_UNKNOWN; bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1); newentrysize = EXT2_DIR_REC_LEN(newdir.e2d_namlen); - - if (ext2_htree_has_idx(dp)) { - error = ext2_htree_add_entry(dvp, &newdir, cnp); - if (error) { - dp->i_flag &= ~IN_E4INDEX; - dp->i_flag |= IN_CHANGE | IN_UPDATE; - } - return (error); - } - - if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) && - !ext2_htree_has_idx(dp)) { - if ((dp->i_size / DIRBLKSIZ) == 1 && - dp->i_offset == DIRBLKSIZ) { - /* - * Making indexed directory when one block is not - * enough to save all entries. - */ - return ext2_htree_create_index(dvp, cnp, &newdir); - } - } - 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.e2d_reclen = 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 = (struct thread *)0; 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 = roundup2(dp->i_size, DIRBLKSIZ); dp->i_flag |= IN_CHANGE; } return (error); } - error = ext2_add_entry(dvp, &newdir); - 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, cnp->cn_thread); - return (error); -} - -/* - * Insert an entry into the directory block. - * Compact the contents. - */ -int -ext2_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry) -{ - struct ext2fs_direct_2 *ep, *nep; - struct inode *dp; - struct buf *bp; - u_int dsize; - int error, loc, newentrysize, spacefree; - char *dirbuf; - - dp = VTOI(dvp); - /* * 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. */ - newentrysize = EXT2_DIR_REC_LEN(entry->e2d_namlen); ep = (struct ext2fs_direct_2 *)dirbuf; dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen); spacefree = ep->e2d_reclen - dsize; for (loc = ep->e2d_reclen; loc < dp->i_count; ) { nep = (struct ext2fs_direct_2 *)(dirbuf + loc); if (ep->e2d_ino) { /* trim the existing slot */ ep->e2d_reclen = dsize; ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); } else { /* overwrite; nothing there; header is ours */ spacefree += dsize; } dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen); spacefree += nep->e2d_reclen - dsize; loc += nep->e2d_reclen; 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->e2d_ino == 0) { if (spacefree + dsize < newentrysize) panic("ext2_direnter: compact1"); - entry->e2d_reclen = spacefree + dsize; + newdir.e2d_reclen = spacefree + dsize; } else { if (spacefree < newentrysize) panic("ext2_direnter: compact2"); - entry->e2d_reclen = spacefree; + newdir.e2d_reclen = spacefree; ep->e2d_reclen = dsize; ep = (struct ext2fs_direct_2 *)((char *)ep + dsize); } - bcopy((caddr_t)entry, (caddr_t)ep, (u_int)newentrysize); + bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize); if (DOINGASYNC(dvp)) { bdwrite(bp); error = 0; } else { 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, cnp->cn_thread); return (error); } /* * Remove a directory entry after a call to namei, using * the parameters which it left in nameidata. The entry * dp->i_offset contains the offset into the directory of the * entry to be eliminated. The dp->i_count field contains the * size of the previous record in the directory. If this * is 0, the first entry is being deleted, so we need only * zero the inode number to mark the entry as free. If the * entry is not the first in the directory, we must reclaim * the space of the now empty record by adding the record size * to the size of the previous entry. */ int ext2_dirremove(struct vnode *dvp, struct componentname *cnp) { struct inode *dp; struct ext2fs_direct_2 *ep, *rep; struct buf *bp; int error; dp = VTOI(dvp); if (dp->i_count == 0) { /* * First entry in block: set d_ino to zero. */ if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) return (error); ep->e2d_ino = 0; error = bwrite(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); } /* * Collapse new free space into previous entry. */ if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0) return (error); /* Set 'rep' to the entry being removed. */ if (dp->i_count == 0) rep = ep; else rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen); ep->e2d_reclen += rep->e2d_reclen; if (DOINGASYNC(dvp) && dp->i_count != 0) bdwrite(bp); else error = bwrite(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); } /* * Rewrite an existing directory entry to point at the inode * supplied. The parameters describing the directory entry are * set up by a call to namei. */ int ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp) { struct buf *bp; struct ext2fs_direct_2 *ep; struct vnode *vdp = ITOV(dp); int error; if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, &bp)) != 0) return (error); ep->e2d_ino = ip->i_number; if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_FTYPE)) ep->e2d_type = DTTOFT(IFTODT(ip->i_mode)); else ep->e2d_type = EXT2_FT_UNKNOWN; error = bwrite(bp); dp->i_flag |= IN_CHANGE | IN_UPDATE; return (error); } /* * Check if a directory is empty or not. * Inode supplied must be locked. * * Using a struct dirtemplate here is not precisely * what we want, but better than using a struct direct. * * NB: does not handle corrupted directories. */ int ext2_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred) { off_t off; struct dirtemplate dbuf; struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf; int error, namlen; ssize_t count; #define MINDIRSIZ (sizeof(struct dirtemplate) / 2) for (off = 0; off < ip->i_size; off += dp->e2d_reclen) { error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, &count, (struct thread *)0); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. */ if (error || count != 0) return (0); /* avoid infinite loops */ if (dp->e2d_reclen == 0) return (0); /* skip empty entries */ if (dp->e2d_ino == 0) continue; /* accept only "." and ".." */ namlen = dp->e2d_namlen; if (namlen > 2) return (0); if (dp->e2d_name[0] != '.') return (0); /* * At this point namlen must be 1 or 2. * 1 implies ".", 2 implies ".." if second * char is also "." */ if (namlen == 1) continue; if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino) continue; return (0); } return (1); } /* * Check if source directory is in the path of the target directory. * Target is supplied locked, source is unlocked. * The target is always vput before returning. */ int ext2_checkpath(struct inode *source, struct inode *target, struct ucred *cred) { struct vnode *vp; int error, namlen; struct dirtemplate dirbuf; vp = ITOV(target); if (target->i_number == source->i_number) { error = EEXIST; goto out; } if (target->i_number == EXT2_ROOTINO) { error = 0; goto out; } for (;;) { if (vp->v_type != VDIR) { error = ENOTDIR; break; } error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, sizeof(struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, NULL); if (error != 0) break; namlen = dirbuf.dotdot_type; /* like ufs little-endian */ if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { error = ENOTDIR; break; } if (dirbuf.dotdot_ino == source->i_number) { error = EINVAL; break; } if (dirbuf.dotdot_ino == EXT2_ROOTINO) break; vput(vp); if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, LK_EXCLUSIVE, &vp)) != 0) { vp = NULL; break; } } out: if (error == ENOTDIR) printf("checkpath: .. not a directory\n"); if (vp != NULL) vput(vp); return (error); } Index: head/sys/fs/ext2fs/ext2_vfsops.c =================================================================== --- head/sys/fs/ext2fs/ext2_vfsops.c (revision 281669) +++ head/sys/fs/ext2fs/ext2_vfsops.c (revision 281670) @@ -1,1115 +1,1101 @@ /*- * modified for EXT2FS support in Lites 1.1 * * Aug 1995, Godmar Back (gback@cs.utah.edu) * University of Utah, Department of Computer Science */ /*- * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * * @(#)ffs_vfsops.c 8.8 (Berkeley) 4/18/94 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ext2_flushfiles(struct mount *mp, int flags, struct thread *td); static int ext2_mountfs(struct vnode *, struct mount *); static int ext2_reload(struct mount *mp, struct thread *td); static int ext2_sbupdate(struct ext2mount *, int); static int ext2_cgupdate(struct ext2mount *, int); static vfs_unmount_t ext2_unmount; static vfs_root_t ext2_root; static vfs_statfs_t ext2_statfs; static vfs_sync_t ext2_sync; static vfs_vget_t ext2_vget; static vfs_fhtovp_t ext2_fhtovp; static vfs_mount_t ext2_mount; MALLOC_DEFINE(M_EXT2NODE, "ext2_node", "EXT2 vnode private part"); static MALLOC_DEFINE(M_EXT2MNT, "ext2_mount", "EXT2 mount structure"); static struct vfsops ext2fs_vfsops = { .vfs_fhtovp = ext2_fhtovp, .vfs_mount = ext2_mount, .vfs_root = ext2_root, /* root inode via vget */ .vfs_statfs = ext2_statfs, .vfs_sync = ext2_sync, .vfs_unmount = ext2_unmount, .vfs_vget = ext2_vget, }; VFS_SET(ext2fs_vfsops, ext2fs, 0); static int ext2_check_sb_compat(struct ext2fs *es, struct cdev *dev, int ronly); static int compute_sb_data(struct vnode * devvp, struct ext2fs * es, struct m_ext2fs * fs); static const char *ext2_opts[] = { "acls", "async", "noatime", "noclusterr", "noclusterw", "noexec", "export", "force", "from", "multilabel", "suiddir", "nosymfollow", "sync", "union", NULL }; /* * VFS Operations. * * mount system call */ static int ext2_mount(struct mount *mp) { struct vfsoptlist *opts; struct vnode *devvp; struct thread *td; struct ext2mount *ump = NULL; struct m_ext2fs *fs; struct nameidata nd, *ndp = &nd; accmode_t accmode; char *path, *fspec; int error, flags, len; td = curthread; opts = mp->mnt_optnew; if (vfs_filteropt(opts, ext2_opts)) return (EINVAL); vfs_getopt(opts, "fspath", (void **)&path, NULL); /* Double-check the length of path.. */ if (strlen(path) >= MAXMNTLEN) return (ENAMETOOLONG); fspec = NULL; error = vfs_getopt(opts, "from", (void **)&fspec, &len); if (!error && fspec[len - 1] != '\0') return (EINVAL); /* * If updating, check whether changing from read-only to * read/write; if there is no device name, that's all we do. */ if (mp->mnt_flag & MNT_UPDATE) { ump = VFSTOEXT2(mp); fs = ump->um_e2fs; error = 0; if (fs->e2fs_ronly == 0 && vfs_flagopt(opts, "ro", NULL, 0)) { error = VFS_SYNC(mp, MNT_WAIT); if (error) return (error); flags = WRITECLOSE; if (mp->mnt_flag & MNT_FORCE) flags |= FORCECLOSE; error = ext2_flushfiles(mp, flags, td); if ( error == 0 && fs->e2fs_wasvalid && ext2_cgupdate(ump, MNT_WAIT) == 0) { fs->e2fs->e2fs_state |= E2FS_ISCLEAN; ext2_sbupdate(ump, MNT_WAIT); } fs->e2fs_ronly = 1; vfs_flagopt(opts, "ro", &mp->mnt_flag, MNT_RDONLY); DROP_GIANT(); g_topology_lock(); g_access(ump->um_cp, 0, -1, 0); g_topology_unlock(); PICKUP_GIANT(); } if (!error && (mp->mnt_flag & MNT_RELOAD)) error = ext2_reload(mp, td); if (error) return (error); devvp = ump->um_devvp; if (fs->e2fs_ronly && !vfs_flagopt(opts, "ro", NULL, 0)) { if (ext2_check_sb_compat(fs->e2fs, devvp->v_rdev, 0)) return (EPERM); /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_ACCESS(devvp, VREAD | VWRITE, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { VOP_UNLOCK(devvp, 0); return (error); } VOP_UNLOCK(devvp, 0); DROP_GIANT(); g_topology_lock(); error = g_access(ump->um_cp, 0, 1, 0); g_topology_unlock(); PICKUP_GIANT(); if (error) return (error); if ((fs->e2fs->e2fs_state & E2FS_ISCLEAN) == 0 || (fs->e2fs->e2fs_state & E2FS_ERRORS)) { if (mp->mnt_flag & MNT_FORCE) { printf( "WARNING: %s was not properly dismounted\n", fs->e2fs_fsmnt); } else { printf( "WARNING: R/W mount of %s denied. Filesystem is not clean - run fsck\n", fs->e2fs_fsmnt); return (EPERM); } } fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; (void)ext2_cgupdate(ump, MNT_WAIT); fs->e2fs_ronly = 0; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_RDONLY; MNT_IUNLOCK(mp); } if (vfs_flagopt(opts, "export", NULL, 0)) { /* Process export requests in vfs_mount.c. */ return (error); } } /* * Not an update, or updating the name: look up the name * and verify that it refers to a sensible disk device. */ if (fspec == NULL) return (EINVAL); NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec, td); if ((error = namei(ndp)) != 0) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); devvp = ndp->ni_vp; if (!vn_isdisk(devvp, &error)) { vput(devvp); return (error); } /* * If mount by non-root, then verify that user has necessary * permissions on the device. * * XXXRW: VOP_ACCESS() enough? */ accmode = VREAD; if ((mp->mnt_flag & MNT_RDONLY) == 0) accmode |= VWRITE; error = VOP_ACCESS(devvp, accmode, td->td_ucred, td); if (error) error = priv_check(td, PRIV_VFS_MOUNT_PERM); if (error) { vput(devvp); return (error); } if ((mp->mnt_flag & MNT_UPDATE) == 0) { error = ext2_mountfs(devvp, mp); } else { if (devvp != ump->um_devvp) { vput(devvp); return (EINVAL); /* needs translation */ } else vput(devvp); } if (error) { vrele(devvp); return (error); } ump = VFSTOEXT2(mp); fs = ump->um_e2fs; /* * Note that this strncpy() is ok because of a check at the start * of ext2_mount(). */ strncpy(fs->e2fs_fsmnt, path, MAXMNTLEN); fs->e2fs_fsmnt[MAXMNTLEN - 1] = '\0'; vfs_mountedfrom(mp, fspec); return (0); } static int ext2_check_sb_compat(struct ext2fs *es, struct cdev *dev, int ronly) { if (es->e2fs_magic != E2FS_MAGIC) { printf("ext2fs: %s: wrong magic number %#x (expected %#x)\n", devtoname(dev), es->e2fs_magic, E2FS_MAGIC); return (1); } if (es->e2fs_rev > E2FS_REV0) { if (es->e2fs_features_incompat & ~(EXT2F_INCOMPAT_SUPP | EXT4F_RO_INCOMPAT_SUPP)) { printf( "WARNING: mount of %s denied due to unsupported optional features\n", devtoname(dev)); return (1); } if (!ronly && (es->e2fs_features_rocompat & ~EXT2F_ROCOMPAT_SUPP)) { printf("WARNING: R/W mount of %s denied due to " "unsupported optional features\n", devtoname(dev)); return (1); } } return (0); } /* * This computes the fields of the ext2_sb_info structure from the * data in the ext2_super_block structure read in. */ static int compute_sb_data(struct vnode *devvp, struct ext2fs *es, struct m_ext2fs *fs) { int db_count, error; int i; int logic_sb_block = 1; /* XXX for now */ struct buf *bp; uint32_t e2fs_descpb; fs->e2fs_bshift = EXT2_MIN_BLOCK_LOG_SIZE + es->e2fs_log_bsize; fs->e2fs_bsize = 1U << fs->e2fs_bshift; fs->e2fs_fsbtodb = es->e2fs_log_bsize + 1; fs->e2fs_qbmask = fs->e2fs_bsize - 1; fs->e2fs_fsize = EXT2_MIN_FRAG_SIZE << es->e2fs_log_fsize; if (fs->e2fs_fsize) fs->e2fs_fpb = fs->e2fs_bsize / fs->e2fs_fsize; fs->e2fs_bpg = es->e2fs_bpg; fs->e2fs_fpg = es->e2fs_fpg; fs->e2fs_ipg = es->e2fs_ipg; if (es->e2fs_rev == E2FS_REV0) { fs->e2fs_isize = E2FS_REV0_INODE_SIZE ; } else { fs->e2fs_isize = es->e2fs_inode_size; /* * Simple sanity check for superblock inode size value. */ if (EXT2_INODE_SIZE(fs) < E2FS_REV0_INODE_SIZE || EXT2_INODE_SIZE(fs) > fs->e2fs_bsize || (fs->e2fs_isize & (fs->e2fs_isize - 1)) != 0) { printf("ext2fs: invalid inode size %d\n", fs->e2fs_isize); return (EIO); } } /* Check for extra isize in big inodes. */ if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_EXTRA_ISIZE) && EXT2_INODE_SIZE(fs) < sizeof(struct ext2fs_dinode)) { printf("ext2fs: no space for extra inode timestamps\n"); return (EINVAL); } fs->e2fs_ipb = fs->e2fs_bsize / EXT2_INODE_SIZE(fs); fs->e2fs_itpg = fs->e2fs_ipg / fs->e2fs_ipb; /* s_resuid / s_resgid ? */ fs->e2fs_gcount = (es->e2fs_bcount - es->e2fs_first_dblock + EXT2_BLOCKS_PER_GROUP(fs) - 1) / EXT2_BLOCKS_PER_GROUP(fs); e2fs_descpb = fs->e2fs_bsize / sizeof(struct ext2_gd); db_count = (fs->e2fs_gcount + e2fs_descpb - 1) / e2fs_descpb; fs->e2fs_gdbcount = db_count; fs->e2fs_gd = malloc(db_count * fs->e2fs_bsize, M_EXT2MNT, M_WAITOK); fs->e2fs_contigdirs = malloc(fs->e2fs_gcount * sizeof(*fs->e2fs_contigdirs), M_EXT2MNT, M_WAITOK | M_ZERO); /* * Adjust logic_sb_block. * Godmar thinks: if the blocksize is greater than 1024, then * the superblock is logically part of block zero. */ if(fs->e2fs_bsize > SBSIZE) logic_sb_block = 0; for (i = 0; i < db_count; i++) { error = bread(devvp , fsbtodb(fs, logic_sb_block + i + 1 ), fs->e2fs_bsize, NOCRED, &bp); if (error) { free(fs->e2fs_contigdirs, M_EXT2MNT); free(fs->e2fs_gd, M_EXT2MNT); brelse(bp); return (error); } e2fs_cgload((struct ext2_gd *)bp->b_data, &fs->e2fs_gd[ i * fs->e2fs_bsize / sizeof(struct ext2_gd)], fs->e2fs_bsize); brelse(bp); bp = NULL; } /* Initialization for the ext2 Orlov allocator variant. */ fs->e2fs_total_dir = 0; for (i = 0; i < fs->e2fs_gcount; i++) fs->e2fs_total_dir += fs->e2fs_gd[i].ext2bgd_ndirs; if (es->e2fs_rev == E2FS_REV0 || !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_LARGEFILE)) fs->e2fs_maxfilesize = 0x7fffffff; - else { - fs->e2fs_maxfilesize = 0xffffffffffff; - if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE)) - fs->e2fs_maxfilesize = 0x7fffffffffffffff; - } - if (es->e4fs_flags & E2FS_UNSIGNED_HASH) { - fs->e2fs_uhash = 3; - } else if ((es->e4fs_flags & E2FS_SIGNED_HASH) == 0) { -#ifdef __CHAR_UNSIGNED__ - es->e4fs_flags |= E2FS_UNSIGNED_HASH; - fs->e2fs_uhash = 3; -#else - es->e4fs_flags |= E2FS_SIGNED_HASH; -#endif - } - + else + fs->e2fs_maxfilesize = 0x7fffffffffffffff; return (0); } /* * Reload all incore data for a filesystem (used after running fsck on * the root filesystem and finding things to fix). The filesystem must * be mounted read-only. * * Things to do to update the mount: * 1) invalidate all cached meta-data. * 2) re-read superblock from disk. * 3) invalidate all cluster summary information. * 4) invalidate all inactive vnodes. * 5) invalidate all cached file data. * 6) re-read inode data for all active vnodes. * XXX we are missing some steps, in particular # 3, this has to be reviewed. */ static int ext2_reload(struct mount *mp, struct thread *td) { struct vnode *vp, *mvp, *devvp; struct inode *ip; struct buf *bp; struct ext2fs *es; struct m_ext2fs *fs; struct csum *sump; int error, i; int32_t *lp; if ((mp->mnt_flag & MNT_RDONLY) == 0) return (EINVAL); /* * Step 1: invalidate all cached meta-data. */ devvp = VFSTOEXT2(mp)->um_devvp; vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); if (vinvalbuf(devvp, 0, 0, 0) != 0) panic("ext2_reload: dirty1"); VOP_UNLOCK(devvp, 0); /* * Step 2: re-read superblock from disk. * constants have been adjusted for ext2 */ if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) return (error); es = (struct ext2fs *)bp->b_data; if (ext2_check_sb_compat(es, devvp->v_rdev, 0) != 0) { brelse(bp); return (EIO); /* XXX needs translation */ } fs = VFSTOEXT2(mp)->um_e2fs; bcopy(bp->b_data, fs->e2fs, sizeof(struct ext2fs)); if((error = compute_sb_data(devvp, es, fs)) != 0) { brelse(bp); return (error); } #ifdef UNKLAR if (fs->fs_sbsize < SBSIZE) bp->b_flags |= B_INVAL; #endif brelse(bp); /* * Step 3: invalidate all cluster summary information. */ if (fs->e2fs_contigsumsize > 0) { lp = fs->e2fs_maxcluster; sump = fs->e2fs_clustersum; for (i = 0; i < fs->e2fs_gcount; i++, sump++) { *lp++ = fs->e2fs_contigsumsize; sump->cs_init = 0; bzero(sump->cs_sum, fs->e2fs_contigsumsize + 1); } } loop: MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { /* * Step 4: invalidate all cached file data. */ if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); goto loop; } if (vinvalbuf(vp, 0, 0, 0)) panic("ext2_reload: dirty2"); /* * Step 5: re-read inode data for all active vnodes. */ ip = VTOI(vp); error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), (int)fs->e2fs_bsize, NOCRED, &bp); if (error) { VOP_UNLOCK(vp, 0); vrele(vp); MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); return (error); } ext2_ei2i((struct ext2fs_dinode *) ((char *)bp->b_data + EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)), ip); brelse(bp); VOP_UNLOCK(vp, 0); vrele(vp); } return (0); } /* * Common code for mount and mountroot. */ static int ext2_mountfs(struct vnode *devvp, struct mount *mp) { struct ext2mount *ump; struct buf *bp; struct m_ext2fs *fs; struct ext2fs *es; struct cdev *dev = devvp->v_rdev; struct g_consumer *cp; struct bufobj *bo; struct csum *sump; int error; int ronly; int i, size; int32_t *lp; int32_t e2fs_maxcontig; ronly = vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0); /* XXX: use VOP_ACESS to check FS perms */ DROP_GIANT(); g_topology_lock(); error = g_vfs_open(devvp, &cp, "ext2fs", ronly ? 0 : 1); g_topology_unlock(); PICKUP_GIANT(); VOP_UNLOCK(devvp, 0); if (error) return (error); /* XXX: should we check for some sectorsize or 512 instead? */ if (((SBSIZE % cp->provider->sectorsize) != 0) || (SBSIZE < cp->provider->sectorsize)) { DROP_GIANT(); g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); PICKUP_GIANT(); return (EINVAL); } bo = &devvp->v_bufobj; bo->bo_private = cp; bo->bo_ops = g_vfs_bufops; if (devvp->v_rdev->si_iosize_max != 0) mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; if (mp->mnt_iosize_max > MAXPHYS) mp->mnt_iosize_max = MAXPHYS; bp = NULL; ump = NULL; if ((error = bread(devvp, SBLOCK, SBSIZE, NOCRED, &bp)) != 0) goto out; es = (struct ext2fs *)bp->b_data; if (ext2_check_sb_compat(es, dev, ronly) != 0) { error = EINVAL; /* XXX needs translation */ goto out; } if ((es->e2fs_state & E2FS_ISCLEAN) == 0 || (es->e2fs_state & E2FS_ERRORS)) { if (ronly || (mp->mnt_flag & MNT_FORCE)) { printf( "WARNING: Filesystem was not properly dismounted\n"); } else { printf( "WARNING: R/W mount denied. Filesystem is not clean - run fsck\n"); error = EPERM; goto out; } } ump = malloc(sizeof(*ump), M_EXT2MNT, M_WAITOK | M_ZERO); /* * I don't know whether this is the right strategy. Note that * we dynamically allocate both an ext2_sb_info and an ext2_super_block * while Linux keeps the super block in a locked buffer. */ ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_EXT2MNT, M_WAITOK); ump->um_e2fs->e2fs = malloc(sizeof(struct ext2fs), M_EXT2MNT, M_WAITOK); mtx_init(EXT2_MTX(ump), "EXT2FS", "EXT2FS Lock", MTX_DEF); bcopy(es, ump->um_e2fs->e2fs, (u_int)sizeof(struct ext2fs)); if ((error = compute_sb_data(devvp, ump->um_e2fs->e2fs, ump->um_e2fs))) goto out; /* * Calculate the maximum contiguous blocks and size of cluster summary * array. In FFS this is done by newfs; however, the superblock * in ext2fs doesn't have these variables, so we can calculate * them here. */ e2fs_maxcontig = MAX(1, MAXPHYS / ump->um_e2fs->e2fs_bsize); ump->um_e2fs->e2fs_contigsumsize = MIN(e2fs_maxcontig, EXT2_MAXCONTIG); if (ump->um_e2fs->e2fs_contigsumsize > 0) { size = ump->um_e2fs->e2fs_gcount * sizeof(int32_t); ump->um_e2fs->e2fs_maxcluster = malloc(size, M_EXT2MNT, M_WAITOK); size = ump->um_e2fs->e2fs_gcount * sizeof(struct csum); ump->um_e2fs->e2fs_clustersum = malloc(size, M_EXT2MNT, M_WAITOK); lp = ump->um_e2fs->e2fs_maxcluster; sump = ump->um_e2fs->e2fs_clustersum; for (i = 0; i < ump->um_e2fs->e2fs_gcount; i++, sump++) { *lp++ = ump->um_e2fs->e2fs_contigsumsize; sump->cs_init = 0; sump->cs_sum = malloc((ump->um_e2fs->e2fs_contigsumsize + 1) * sizeof(int32_t), M_EXT2MNT, M_WAITOK | M_ZERO); } } brelse(bp); bp = NULL; fs = ump->um_e2fs; fs->e2fs_ronly = ronly; /* ronly is set according to mnt_flags */ /* * If the fs is not mounted read-only, make sure the super block is * always written back on a sync(). */ fs->e2fs_wasvalid = fs->e2fs->e2fs_state & E2FS_ISCLEAN ? 1 : 0; if (ronly == 0) { fs->e2fs_fmod = 1; /* mark it modified */ fs->e2fs->e2fs_state &= ~E2FS_ISCLEAN; /* set fs invalid */ } mp->mnt_data = ump; mp->mnt_stat.f_fsid.val[0] = dev2udev(dev); mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN; MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; MNT_IUNLOCK(mp); ump->um_mountp = mp; ump->um_dev = dev; ump->um_devvp = devvp; ump->um_bo = &devvp->v_bufobj; ump->um_cp = cp; /* * Setting those two parameters allowed us to use * ufs_bmap w/o changse! */ ump->um_nindir = EXT2_ADDR_PER_BLOCK(fs); ump->um_bptrtodb = fs->e2fs->e2fs_log_bsize + 1; ump->um_seqinc = EXT2_FRAGS_PER_BLOCK(fs); if (ronly == 0) ext2_sbupdate(ump, MNT_WAIT); /* * Initialize filesystem stat information in mount struct. */ MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED | MNTK_USES_BCACHE; MNT_IUNLOCK(mp); return (0); out: if (bp) brelse(bp); if (cp != NULL) { DROP_GIANT(); g_topology_lock(); g_vfs_close(cp); g_topology_unlock(); PICKUP_GIANT(); } if (ump) { mtx_destroy(EXT2_MTX(ump)); free(ump->um_e2fs->e2fs_gd, M_EXT2MNT); free(ump->um_e2fs->e2fs_contigdirs, M_EXT2MNT); free(ump->um_e2fs->e2fs, M_EXT2MNT); free(ump->um_e2fs, M_EXT2MNT); free(ump, M_EXT2MNT); mp->mnt_data = NULL; } return (error); } /* * Unmount system call. */ static int ext2_unmount(struct mount *mp, int mntflags) { struct ext2mount *ump; struct m_ext2fs *fs; struct csum *sump; int error, flags, i, ronly; flags = 0; if (mntflags & MNT_FORCE) { if (mp->mnt_flag & MNT_ROOTFS) return (EINVAL); flags |= FORCECLOSE; } if ((error = ext2_flushfiles(mp, flags, curthread)) != 0) return (error); ump = VFSTOEXT2(mp); fs = ump->um_e2fs; ronly = fs->e2fs_ronly; if (ronly == 0 && ext2_cgupdate(ump, MNT_WAIT) == 0) { if (fs->e2fs_wasvalid) fs->e2fs->e2fs_state |= E2FS_ISCLEAN; ext2_sbupdate(ump, MNT_WAIT); } DROP_GIANT(); g_topology_lock(); g_vfs_close(ump->um_cp); g_topology_unlock(); PICKUP_GIANT(); vrele(ump->um_devvp); sump = fs->e2fs_clustersum; for (i = 0; i < fs->e2fs_gcount; i++, sump++) free(sump->cs_sum, M_EXT2MNT); free(fs->e2fs_clustersum, M_EXT2MNT); free(fs->e2fs_maxcluster, M_EXT2MNT); free(fs->e2fs_gd, M_EXT2MNT); free(fs->e2fs_contigdirs, M_EXT2MNT); free(fs->e2fs, M_EXT2MNT); free(fs, M_EXT2MNT); free(ump, M_EXT2MNT); mp->mnt_data = NULL; MNT_ILOCK(mp); mp->mnt_flag &= ~MNT_LOCAL; MNT_IUNLOCK(mp); return (error); } /* * Flush out all the files in a filesystem. */ static int ext2_flushfiles(struct mount *mp, int flags, struct thread *td) { int error; error = vflush(mp, 0, flags, td); return (error); } /* * Get filesystem statistics. */ int ext2_statfs(struct mount *mp, struct statfs *sbp) { struct ext2mount *ump; struct m_ext2fs *fs; uint32_t overhead, overhead_per_group, ngdb; int i, ngroups; ump = VFSTOEXT2(mp); fs = ump->um_e2fs; if (fs->e2fs->e2fs_magic != E2FS_MAGIC) panic("ext2_statfs"); /* * Compute the overhead (FS structures) */ overhead_per_group = 1 /* block bitmap */ + 1 /* inode bitmap */ + fs->e2fs_itpg; overhead = fs->e2fs->e2fs_first_dblock + fs->e2fs_gcount * overhead_per_group; if (fs->e2fs->e2fs_rev > E2FS_REV0 && fs->e2fs->e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSESUPER) { for (i = 0, ngroups = 0; i < fs->e2fs_gcount; i++) { if (cg_has_sb(i)) ngroups++; } } else { ngroups = fs->e2fs_gcount; } ngdb = fs->e2fs_gdbcount; if (fs->e2fs->e2fs_rev > E2FS_REV0 && fs->e2fs->e2fs_features_compat & EXT2F_COMPAT_RESIZE) ngdb += fs->e2fs->e2fs_reserved_ngdb; overhead += ngroups * (1 /* superblock */ + ngdb); sbp->f_bsize = EXT2_FRAG_SIZE(fs); sbp->f_iosize = EXT2_BLOCK_SIZE(fs); sbp->f_blocks = fs->e2fs->e2fs_bcount - overhead; sbp->f_bfree = fs->e2fs->e2fs_fbcount; sbp->f_bavail = sbp->f_bfree - fs->e2fs->e2fs_rbcount; sbp->f_files = fs->e2fs->e2fs_icount; sbp->f_ffree = fs->e2fs->e2fs_ficount; return (0); } /* * Go through the disk queues to initiate sandbagged IO; * go through the inodes to write those that have been modified; * initiate the writing of the super block if it has been modified. * * Note: we are always called with the filesystem marked `MPBUSY'. */ static int ext2_sync(struct mount *mp, int waitfor) { struct vnode *mvp, *vp; struct thread *td; struct inode *ip; struct ext2mount *ump = VFSTOEXT2(mp); struct m_ext2fs *fs; int error, allerror = 0; td = curthread; fs = ump->um_e2fs; if (fs->e2fs_fmod != 0 && fs->e2fs_ronly != 0) { /* XXX */ printf("fs = %s\n", fs->e2fs_fsmnt); panic("ext2_sync: rofs mod"); } /* * Write back each (modified) inode. */ loop: MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { if (vp->v_type == VNON) { VI_UNLOCK(vp); continue; } ip = VTOI(vp); if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && (vp->v_bufobj.bo_dirty.bv_cnt == 0 || waitfor == MNT_LAZY)) { VI_UNLOCK(vp); continue; } error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, td); if (error) { if (error == ENOENT) { MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); goto loop; } continue; } if ((error = VOP_FSYNC(vp, waitfor, td)) != 0) allerror = error; VOP_UNLOCK(vp, 0); vrele(vp); } /* * Force stale filesystem control information to be flushed. */ if (waitfor != MNT_LAZY) { vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); if ((error = VOP_FSYNC(ump->um_devvp, waitfor, td)) != 0) allerror = error; VOP_UNLOCK(ump->um_devvp, 0); } /* * Write back modified superblock. */ if (fs->e2fs_fmod != 0) { fs->e2fs_fmod = 0; fs->e2fs->e2fs_wtime = time_second; if ((error = ext2_cgupdate(ump, waitfor)) != 0) allerror = error; } return (allerror); } /* * Look up an EXT2FS dinode number to find its incore vnode, otherwise read it * in from disk. If it is in core, wait for the lock bit to clear, then * return the inode locked. Detection and handling of mount points must be * done by the calling routine. */ static int ext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) { struct m_ext2fs *fs; struct inode *ip; struct ext2mount *ump; struct buf *bp; struct vnode *vp; struct thread *td; int i, error; int used_blocks; td = curthread; error = vfs_hash_get(mp, ino, flags, td, vpp, NULL, NULL); if (error || *vpp != NULL) return (error); ump = VFSTOEXT2(mp); ip = malloc(sizeof(struct inode), M_EXT2NODE, M_WAITOK | M_ZERO); /* Allocate a new vnode/inode. */ if ((error = getnewvnode("ext2fs", mp, &ext2_vnodeops, &vp)) != 0) { *vpp = NULL; free(ip, M_EXT2NODE); return (error); } vp->v_data = ip; ip->i_vnode = vp; ip->i_e2fs = fs = ump->um_e2fs; ip->i_ump = ump; ip->i_number = ino; lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); error = insmntque(vp, mp); if (error != 0) { free(ip, M_EXT2NODE); *vpp = NULL; return (error); } error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); if (error || *vpp != NULL) return (error); /* Read in the disk contents for the inode, copy into the inode. */ if ((error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { /* * The inode does not contain anything useful, so it would * be misleading to leave it on its hash chain. With mode * still zero, it will be unlinked and returned to the free * list by vput(). */ brelse(bp); vput(vp); *vpp = NULL; return (error); } /* convert ext2 inode to dinode */ ext2_ei2i((struct ext2fs_dinode *) ((char *)bp->b_data + EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ino)), ip); ip->i_block_group = ino_to_cg(fs, ino); ip->i_next_alloc_block = 0; ip->i_next_alloc_goal = 0; /* * Now we want to make sure that block pointers for unused * blocks are zeroed out - ext2_balloc depends on this * although for regular files and directories only * * If IN_E4EXTENTS is enabled, unused blocks are not zeroed * out because we could corrupt the extent tree. */ if (!(ip->i_flag & IN_E4EXTENTS) && (S_ISDIR(ip->i_mode) || S_ISREG(ip->i_mode))) { used_blocks = (ip->i_size+fs->e2fs_bsize-1) / fs->e2fs_bsize; for (i = used_blocks; i < EXT2_NDIR_BLOCKS; i++) ip->i_db[i] = 0; } #ifdef EXT2FS_DEBUG ext2_print_inode(ip); #endif bqrelse(bp); /* * Initialize the vnode from the inode, check for aliases. * Note that the underlying vnode may have changed. */ if ((error = ext2_vinit(mp, &ext2_fifoops, &vp)) != 0) { vput(vp); *vpp = NULL; return (error); } /* * Finish inode initialization. */ /* * Set up a generation number for this inode if it does not * already have one. This should only happen on old filesystems. */ if (ip->i_gen == 0) { ip->i_gen = random() + 1; if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) ip->i_flag |= IN_MODIFIED; } *vpp = vp; return (0); } /* * File handle to vnode * * Have to be really careful about stale file handles: * - check that the inode number is valid * - call ext2_vget() to get the locked inode * - check for an unallocated inode (i_mode == 0) * - check that the given client host has export rights and return * those rights via. exflagsp and credanonp */ static int ext2_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp) { struct inode *ip; struct ufid *ufhp; struct vnode *nvp; struct m_ext2fs *fs; int error; ufhp = (struct ufid *)fhp; fs = VFSTOEXT2(mp)->um_e2fs; if (ufhp->ufid_ino < EXT2_ROOTINO || ufhp->ufid_ino > fs->e2fs_gcount * fs->e2fs->e2fs_ipg) return (ESTALE); error = VFS_VGET(mp, ufhp->ufid_ino, LK_EXCLUSIVE, &nvp); if (error) { *vpp = NULLVP; return (error); } ip = VTOI(nvp); if (ip->i_mode == 0 || ip->i_gen != ufhp->ufid_gen || ip->i_nlink <= 0) { vput(nvp); *vpp = NULLVP; return (ESTALE); } *vpp = nvp; vnode_create_vobject(*vpp, 0, curthread); return (0); } /* * Write a superblock and associated information back to disk. */ static int ext2_sbupdate(struct ext2mount *mp, int waitfor) { struct m_ext2fs *fs = mp->um_e2fs; struct ext2fs *es = fs->e2fs; struct buf *bp; int error = 0; bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0, 0); bcopy((caddr_t)es, bp->b_data, (u_int)sizeof(struct ext2fs)); if (waitfor == MNT_WAIT) error = bwrite(bp); else bawrite(bp); /* * The buffers for group descriptors, inode bitmaps and block bitmaps * are not busy at this point and are (hopefully) written by the * usual sync mechanism. No need to write them here. */ return (error); } int ext2_cgupdate(struct ext2mount *mp, int waitfor) { struct m_ext2fs *fs = mp->um_e2fs; struct buf *bp; int i, error = 0, allerror = 0; allerror = ext2_sbupdate(mp, waitfor); for (i = 0; i < fs->e2fs_gdbcount; i++) { bp = getblk(mp->um_devvp, fsbtodb(fs, fs->e2fs->e2fs_first_dblock + 1 /* superblock */ + i), fs->e2fs_bsize, 0, 0, 0); e2fs_cgsave(&fs->e2fs_gd[ i * fs->e2fs_bsize / sizeof(struct ext2_gd)], (struct ext2_gd *)bp->b_data, fs->e2fs_bsize); if (waitfor == MNT_WAIT) error = bwrite(bp); else bawrite(bp); } if (!allerror && error) allerror = error; return (allerror); } /* * Return the root of a filesystem. */ static int ext2_root(struct mount *mp, int flags, struct vnode **vpp) { struct vnode *nvp; int error; error = VFS_VGET(mp, EXT2_ROOTINO, LK_EXCLUSIVE, &nvp); if (error) return (error); *vpp = nvp; return (0); } Index: head/sys/fs/ext2fs/ext2fs.h =================================================================== --- head/sys/fs/ext2fs/ext2fs.h (revision 281669) +++ head/sys/fs/ext2fs/ext2fs.h (revision 281670) @@ -1,299 +1,291 @@ /*- * modified for EXT2FS support in Lites 1.1 * * Aug 1995, Godmar Back (gback@cs.utah.edu) * University of Utah, Department of Computer Science * * $FreeBSD$ */ /*- * Copyright (c) 2009 Aditya Sarawgi * 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 AUTHOR 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 AUTHOR 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 _FS_EXT2FS_EXT2FS_H_ #define _FS_EXT2FS_EXT2FS_H_ #include /* * Super block for an ext2fs file system. */ struct ext2fs { uint32_t e2fs_icount; /* Inode count */ uint32_t e2fs_bcount; /* blocks count */ uint32_t e2fs_rbcount; /* reserved blocks count */ uint32_t e2fs_fbcount; /* free blocks count */ uint32_t e2fs_ficount; /* free inodes count */ uint32_t e2fs_first_dblock; /* first data block */ uint32_t e2fs_log_bsize; /* block size = 1024*(2^e2fs_log_bsize) */ uint32_t e2fs_log_fsize; /* fragment size */ uint32_t e2fs_bpg; /* blocks per group */ uint32_t e2fs_fpg; /* frags per group */ uint32_t e2fs_ipg; /* inodes per group */ uint32_t e2fs_mtime; /* mount time */ uint32_t e2fs_wtime; /* write time */ uint16_t e2fs_mnt_count; /* mount count */ uint16_t e2fs_max_mnt_count; /* max mount count */ uint16_t e2fs_magic; /* magic number */ uint16_t e2fs_state; /* file system state */ uint16_t e2fs_beh; /* behavior on errors */ uint16_t e2fs_minrev; /* minor revision level */ uint32_t e2fs_lastfsck; /* time of last fsck */ uint32_t e2fs_fsckintv; /* max time between fscks */ uint32_t e2fs_creator; /* creator OS */ uint32_t e2fs_rev; /* revision level */ uint16_t e2fs_ruid; /* default uid for reserved blocks */ uint16_t e2fs_rgid; /* default gid for reserved blocks */ /* EXT2_DYNAMIC_REV superblocks */ uint32_t e2fs_first_ino; /* first non-reserved inode */ uint16_t e2fs_inode_size; /* size of inode structure */ uint16_t e2fs_block_group_nr; /* block grp number of this sblk*/ uint32_t e2fs_features_compat; /* compatible feature set */ uint32_t e2fs_features_incompat; /* incompatible feature set */ uint32_t e2fs_features_rocompat; /* RO-compatible feature set */ uint8_t e2fs_uuid[16]; /* 128-bit uuid for volume */ char e2fs_vname[16]; /* volume name */ char e2fs_fsmnt[64]; /* name mounted on */ uint32_t e2fs_algo; /* For compression */ uint8_t e2fs_prealloc; /* # of blocks for old prealloc */ uint8_t e2fs_dir_prealloc; /* # of blocks for old prealloc dirs */ uint16_t e2fs_reserved_ngdb; /* # of reserved gd blocks for resize */ char e3fs_journal_uuid[16]; /* uuid of journal superblock */ uint32_t e3fs_journal_inum; /* inode number of journal file */ uint32_t e3fs_journal_dev; /* device number of journal file */ uint32_t e3fs_last_orphan; /* start of list of inodes to delete */ uint32_t e3fs_hash_seed[4]; /* HTREE hash seed */ char e3fs_def_hash_version; /* Default hash version to use */ char e3fs_reserved_char_pad; uint32_t e3fs_default_mount_opts; uint32_t e3fs_first_meta_bg; /* First metablock block group */ uint32_t e3fs_mkfs_time; /* when the fs was created */ uint32_t e3fs_jnl_blks[17]; /* backup of the journal inode */ uint32_t e4fs_bcount_hi; /* block count */ uint32_t e4fs_rbcount_hi; /* reserved blocks count */ uint32_t e4fs_fbcount_hi; /* free blocks count */ uint16_t e4fs_min_extra_isize;/* all inodes have at least some bytes */ uint16_t e4fs_want_extra_isize; /* inodes must reserve some bytes */ uint32_t e4fs_flags; /* miscellaneous flags */ uint16_t e4fs_raid_stride; /* RAID stride */ uint16_t e4fs_mmpintv; /* number of seconds to wait in MMP checking */ uint64_t e4fs_mmpblk; /* block for multi-mount protection */ uint32_t e4fs_raid_stripe_wid;/* blocks on all data disks (N * stride) */ uint8_t e4fs_log_gpf; /* FLEX_BG group size */ uint8_t e4fs_char_pad2; uint16_t e4fs_pad; uint32_t reserved2[162]; /* Padding to the end of the block */ }; /* * The path name on which the file system is mounted is maintained * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in * the super block for this name. */ #define MAXMNTLEN 512 /* * In-Memory Superblock */ struct m_ext2fs { struct ext2fs * e2fs; char e2fs_fsmnt[MAXMNTLEN];/* name mounted on */ char e2fs_ronly; /* mounted read-only flag */ char e2fs_fmod; /* super block modified flag */ uint32_t e2fs_bsize; /* Block size */ uint32_t e2fs_bshift; /* calc of logical block no */ uint32_t e2fs_bpg; /* Number of blocks per group */ int64_t e2fs_qbmask; /* = s_blocksize -1 */ uint32_t e2fs_fsbtodb; /* Shift to get disk block */ uint32_t e2fs_ipg; /* Number of inodes per group */ uint32_t e2fs_ipb; /* Number of inodes per block */ uint32_t e2fs_itpg; /* Number of inode table per group */ uint32_t e2fs_fsize; /* Size of fragments per block */ uint32_t e2fs_fpb; /* Number of fragments per block */ uint32_t e2fs_fpg; /* Number of fragments per group */ uint32_t e2fs_gdbcount; /* Number of group descriptors */ uint32_t e2fs_gcount; /* Number of groups */ uint32_t e2fs_isize; /* Size of inode */ uint32_t e2fs_total_dir; /* Total number of directories */ uint8_t *e2fs_contigdirs; /* (u) # of contig. allocated dirs */ char e2fs_wasvalid; /* valid at mount time */ off_t e2fs_maxfilesize; struct ext2_gd *e2fs_gd; /* Group Descriptors */ int32_t e2fs_contigsumsize; /* size of cluster summary array */ int32_t *e2fs_maxcluster; /* max cluster in each cyl group */ struct csum *e2fs_clustersum; /* cluster summary in each cyl group */ - int32_t e2fs_uhash; /* 3 if hash should be signed, 0 if not */ }; /* cluster summary information */ struct csum { int8_t cs_init; /* cluster summary has been initialized */ int32_t *cs_sum; /* cluster summary array */ }; /* * The second extended file system magic number */ #define E2FS_MAGIC 0xEF53 /* * Revision levels */ #define E2FS_REV0 0 /* The good old (original) format */ #define E2FS_REV1 1 /* V2 format w/ dynamic inode sizes */ #define E2FS_REV0_INODE_SIZE 128 /* * compatible/incompatible features */ #define EXT2F_COMPAT_PREALLOC 0x0001 #define EXT2F_COMPAT_HASJOURNAL 0x0004 #define EXT2F_COMPAT_RESIZE 0x0010 #define EXT2F_COMPAT_DIRHASHINDEX 0x0020 #define EXT2F_ROCOMPAT_SPARSESUPER 0x0001 #define EXT2F_ROCOMPAT_LARGEFILE 0x0002 #define EXT2F_ROCOMPAT_BTREE_DIR 0x0004 #define EXT2F_ROCOMPAT_HUGE_FILE 0x0008 #define EXT2F_ROCOMPAT_GDT_CSUM 0x0010 #define EXT2F_ROCOMPAT_DIR_NLINK 0x0020 #define EXT2F_ROCOMPAT_EXTRA_ISIZE 0x0040 #define EXT2F_INCOMPAT_COMP 0x0001 #define EXT2F_INCOMPAT_FTYPE 0x0002 #define EXT2F_INCOMPAT_META_BG 0x0010 #define EXT2F_INCOMPAT_EXTENTS 0x0040 #define EXT2F_INCOMPAT_64BIT 0x0080 #define EXT2F_INCOMPAT_MMP 0x0100 #define EXT2F_INCOMPAT_FLEX_BG 0x0200 /* * Features supported in this implementation * * We support the following REV1 features: * - EXT2F_ROCOMPAT_SPARSESUPER * - EXT2F_ROCOMPAT_LARGEFILE * - EXT2F_ROCOMPAT_EXTRA_ISIZE * - EXT2F_INCOMPAT_FTYPE * * We partially (read-only) support the following EXT4 features: * - EXT2F_ROCOMPAT_HUGE_FILE * - EXT2F_INCOMPAT_EXTENTS * * We do not support these EXT4 features but they are irrelevant * for read-only support: * - EXT2F_INCOMPAT_FLEX_BG * - EXT2F_INCOMPAT_META_BG */ -#define EXT2F_COMPAT_SUPP EXT2F_COMPAT_DIRHASHINDEX #define EXT2F_ROCOMPAT_SUPP (EXT2F_ROCOMPAT_SPARSESUPER | \ EXT2F_ROCOMPAT_LARGEFILE | \ EXT2F_ROCOMPAT_EXTRA_ISIZE) #define EXT2F_INCOMPAT_SUPP EXT2F_INCOMPAT_FTYPE #define EXT4F_RO_INCOMPAT_SUPP (EXT2F_INCOMPAT_EXTENTS | \ EXT2F_INCOMPAT_FLEX_BG | \ EXT2F_INCOMPAT_META_BG ) /* Assume that user mode programs are passing in an ext2fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test * macros from user land. */ #define EXT2_SB(sb) (sb) /* * Feature set definitions */ #define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->e2fs->e2fs_features_compat & htole32(mask) ) #define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->e2fs->e2fs_features_rocompat & htole32(mask) ) #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->e2fs->e2fs_features_incompat & htole32(mask) ) /* * File clean flags */ #define E2FS_ISCLEAN 0x0001 /* Unmounted cleanly */ #define E2FS_ERRORS 0x0002 /* Errors detected */ - -/* - * Filesystem miscellaneous flags - */ -#define E2FS_SIGNED_HASH 0x0001 -#define E2FS_UNSIGNED_HASH 0x0002 /* ext2 file system block group descriptor */ struct ext2_gd { uint32_t ext2bgd_b_bitmap; /* blocks bitmap block */ uint32_t ext2bgd_i_bitmap; /* inodes bitmap block */ uint32_t ext2bgd_i_tables; /* inodes table block */ uint16_t ext2bgd_nbfree; /* number of free blocks */ uint16_t ext2bgd_nifree; /* number of free inodes */ uint16_t ext2bgd_ndirs; /* number of directories */ uint16_t ext4bgd_flags; /* block group flags */ uint32_t ext4bgd_x_bitmap; /* snapshot exclusion bitmap loc. */ uint16_t ext4bgd_b_bmap_csum; /* block bitmap checksum */ uint16_t ext4bgd_i_bmap_csum; /* inode bitmap checksum */ uint16_t ext4bgd_i_unused; /* unused inode count */ uint16_t ext4bgd_csum; /* group descriptor checksum */ }; /* EXT2FS metadatas are stored in little-endian byte order. These macros * helps reading these metadatas */ #define e2fs_cgload(old, new, size) memcpy((new), (old), (size)); #define e2fs_cgsave(old, new, size) memcpy((new), (old), (size)); /* * Macro-instructions used to manage several block sizes */ #define EXT2_MAX_BLOCK_SIZE 4096 #define EXT2_MIN_BLOCK_LOG_SIZE 10 #define EXT2_BLOCK_SIZE(s) ((s)->e2fs_bsize) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(uint32_t)) #define EXT2_INODE_SIZE(s) (EXT2_SB(s)->e2fs_isize) /* * Macro-instructions used to manage fragments */ #define EXT2_MIN_FRAG_SIZE 1024 #define EXT2_MAX_FRAG_SIZE 4096 #define EXT2_MIN_FRAG_LOG_SIZE 10 #define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->e2fs_fsize) #define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->e2fs_fpb) /* * Macro-instructions used to manage group descriptors */ #define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->e2fs_bpg) #endif /* !_FS_EXT2FS_EXT2FS_H_ */ Index: head/sys/modules/ext2fs/Makefile =================================================================== --- head/sys/modules/ext2fs/Makefile (revision 281669) +++ head/sys/modules/ext2fs/Makefile (revision 281670) @@ -1,10 +1,10 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../fs/ext2fs KMOD= ext2fs SRCS= opt_ddb.h opt_directio.h opt_quota.h opt_suiddir.h vnode_if.h \ - ext2_alloc.c ext2_balloc.c ext2_bmap.c ext2_extents.c ext2_hash.c \ - ext2_htree.c ext2_inode.c ext2_inode_cnv.c ext2_lookup.c ext2_subr.c \ + ext2_alloc.c ext2_balloc.c ext2_bmap.c ext2_extents.c \ + ext2_inode.c ext2_inode_cnv.c ext2_lookup.c ext2_subr.c \ ext2_vfsops.c ext2_vnops.c .include