From: Matthew Dillon Date: Tue, 24 Sep 2013 22:14:26 +0000 (-0700) Subject: hammer2 - Add hammer2 boot support X-Git-Tag: v3.7.0~300 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/cdfd0a3e265fcdd4e2200620157aaeebe6bfd8f8 hammer2 - Add hammer2 boot support * Add hammer2 boot loader support. The hammer2 boot loader accesses the hammer2 mount starting at the super-root. To use hammer2 boot support you need to have a PFS called 'boot', which will be seen as "/boot" by the boot loader. The 'boot' PFS can be created either by creating a boot-specific H2 partition with 'newfs_hammer2 -L boot ...', or by using 'hammer2 pfs-create boot' to create a PFS called 'boot'. You can access any super-root if desired. * Since boot is a super-root you can mix boot PFS's and other PFS's on the same hammer2 filesystem, including creating a boot PFS far later on. * WARNING!!!!! The 'boot' PFS must be configured without compression. i.e. 'hammer2 setcomp autozero /boot' (assuming you've mounted it on /boot), before you install anything. The newfs_hammer2 utility will automatically handle this if you specify a label name of 'boot' (i.e. -L boot). * WARNING!!!!! The BIOS may not be able to reliably access very high sector numbers on large hard drives, so to be safe we recommend creating a small 'a' partition anyway, unless the drive / main hammer2 filesystem is small enough to not cause problems. * Add %qx support to boot2's mini printf(). * Fix ipdata->comp_algo propagation bug in hammer2_inode_create() * newfs_hammer2 and the pfs_create ioctl will set comp_algo to AUTOZERO for PFS's called "boot" automatically. --- diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile index 46ee091df7..d5cb42cf0e 100644 --- a/lib/libstand/Makefile +++ b/lib/libstand/Makefile @@ -48,6 +48,9 @@ SRCS+= bcmp.c bcopy.c bzero.c ffs.c index.c memccpy.c memchr.c memcmp.c \ SRCS+= ucmpdi2.c .endif +.PATH: ${.CURDIR}/../../sys/libkern +SRCS+= icrc32.c + # _setjmp/_longjmp .if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "x86_64" .PATH: ${.CURDIR}/i386 @@ -89,7 +92,8 @@ SRCS+= ufs.c nfs.c cd9660.c tftp.c zipfs.c bzipfs.c gzipfs.c SRCS+= netif.c nfs.c SRCS+= dosfs.c ext2fs.c SRCS+= splitfs.c -SRCS+= hammerread.c +SRCS+= hammer1.c +SRCS+= hammer2.c .include diff --git a/lib/libstand/hammer1.c b/lib/libstand/hammer1.c new file mode 100644 index 0000000000..785c13c774 --- /dev/null +++ b/lib/libstand/hammer1.c @@ -0,0 +1,1109 @@ +/* + * Copyright (c) 2008 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Simon Schubert + * and Matthew Dillon + * + * 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. + * 3. Neither the name of The DragonFly Project 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + */ + +/* + * This file is being used by boot2 and libstand (loader). + * Compile with -DTESTING to obtain a binary. + */ + + +#if !defined(BOOT2) && !defined(TESTING) +#define LIBSTAND 1 +#endif + +#ifdef BOOT2 +#include "boot2.h" +#else +#include +#include +#include +#endif + +#ifdef TESTING +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef LIBSTAND +#include "stand.h" +#endif + +#include + +#ifndef BOOT2 +struct blockentry { + hammer_off_t off; + int use; + char *data; +}; + +#ifdef TESTING +#define NUMCACHE 16 +#else +#define NUMCACHE 6 +#endif + +struct hfs { +#ifdef TESTING + int fd; +#else // libstand + struct open_file *f; +#endif + hammer_off_t root; + int64_t buf_beg; + int64_t last_dir_ino; + u_int8_t last_dir_cap_flags; + int lru; + struct blockentry cache[NUMCACHE]; +}; + +static void * +hread(struct hfs *hfs, hammer_off_t off) +{ + hammer_off_t boff = off & ~HAMMER_BUFMASK64; + + boff &= HAMMER_OFF_LONG_MASK; + + if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX) + boff += hfs->buf_beg; + + struct blockentry *be = NULL; + for (int i = 0; i < NUMCACHE; i++) { + if (be == NULL || be->use > hfs->cache[i].use) + be = &hfs->cache[i]; + if (hfs->cache[i].off == boff) { + be = &hfs->cache[i]; + break; + } + } + if (be->off != boff) { + // Didn't find any match + be->off = boff; +#ifdef TESTING + ssize_t res = pread(hfs->fd, be->data, HAMMER_BUFSIZE, + boff & HAMMER_OFF_SHORT_MASK); + if (res != HAMMER_BUFSIZE) + err(1, "short read on off %llx", boff); +#else // libstand + size_t rlen; + int rv = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ, + boff >> DEV_BSHIFT, HAMMER_BUFSIZE, + be->data, &rlen); + if (rv || rlen != HAMMER_BUFSIZE) + return (NULL); +#endif + } + + be->use = ++hfs->lru; + return &be->data[off & HAMMER_BUFMASK]; +} + +#else /* BOOT2 */ + +struct hammer_dmadat { + struct boot2_dmadat boot2; + char buf[HAMMER_BUFSIZE]; +}; + +#define fsdmadat ((struct hammer_dmadat *)boot2_dmadat) + +struct hfs { + hammer_off_t root; + int64_t last_dir_ino; + u_int8_t last_dir_cap_flags; + int64_t buf_beg; +}; + +static void * +hread(struct hfs *hfs, hammer_off_t off) +{ + char *buf = fsdmadat->buf; + + hammer_off_t boff = off & ~HAMMER_BUFMASK64; + boff &= HAMMER_OFF_LONG_MASK; + if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX) + boff += hfs->buf_beg; + boff &= HAMMER_OFF_SHORT_MASK; + boff >>= DEV_BSHIFT; + if (dskread(buf, boff, HAMMER_BUFSIZE >> DEV_BSHIFT)) + return (NULL); + return (&buf[off & HAMMER_BUFMASK]); +} + +static void +bzero(void *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) + ((char *)buf)[i] = 0; +} + +static void +bcopy(void *src, void *dst, size_t size) +{ + memcpy(dst, src, size); +} + +static size_t +strlen(const char *s) +{ + size_t l = 0; + for (; *s != 0; s++) + l++; + return (l); +} + +static int +memcmp(const void *a, const void *b, size_t len) +{ + for (size_t p = 0; p < len; p++) { + int r = ((const char *)a)[p] - ((const char *)b)[p]; + if (r != 0) + return (r); + } + + return (0); +} + +#endif + +/* + * (from hammer_btree.c) + * + * Compare two B-Tree elements, return -N, 0, or +N (e.g. similar to strcmp). + * + * Note that for this particular function a return value of -1, 0, or +1 + * can denote a match if create_tid is otherwise discounted. A create_tid + * of zero is considered to be 'infinity' in comparisons. + * + * See also hammer_rec_rb_compare() and hammer_rec_cmp() in hammer_object.c. + */ +static int +hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2) +{ + if (key1->localization < key2->localization) + return(-5); + if (key1->localization > key2->localization) + return(5); + + if (key1->obj_id < key2->obj_id) + return(-4); + if (key1->obj_id > key2->obj_id) + return(4); + + if (key1->rec_type < key2->rec_type) + return(-3); + if (key1->rec_type > key2->rec_type) + return(3); + + if (key1->key < key2->key) + return(-2); + if (key1->key > key2->key) + return(2); + + /* + * A create_tid of zero indicates a record which is undeletable + * and must be considered to have a value of positive infinity. + */ + if (key1->create_tid == 0) { + if (key2->create_tid == 0) + return(0); + return(1); + } + if (key2->create_tid == 0) + return(-1); + if (key1->create_tid < key2->create_tid) + return(-1); + if (key1->create_tid > key2->create_tid) + return(1); + return(0); +} + +/* + * Heuristical search for the first element whos comparison is <= 1. May + * return an index whos compare result is > 1 but may only return an index + * whos compare result is <= 1 if it is the first element with that result. + */ +static int +hammer_btree_search_node(hammer_base_elm_t elm, hammer_node_ondisk_t node) +{ + int b; + int s; + int i; + int r; + + /* + * Don't bother if the node does not have very many elements + */ + b = 0; + s = node->count; + while (s - b > 4) { + i = b + (s - b) / 2; + r = hammer_btree_cmp(elm, &node->elms[i].leaf.base); + if (r <= 1) { + s = i; + } else { + b = i; + } + } + return(b); +} + +#if 0 +/* + * (from hammer_subs.c) + * + * Return a namekey hash. The 64 bit namekey hash consists of a 32 bit + * crc in the MSB and 0 in the LSB. The caller will use the low bits to + * generate a unique key and will scan all entries with the same upper + * 32 bits when issuing a lookup. + * + * We strip bit 63 in order to provide a positive key, this way a seek + * offset of 0 will represent the base of the directory. + * + * This function can never return 0. We use the MSB-0 space to synthesize + * artificial directory entries such as "." and "..". + */ +static int64_t +hammer_directory_namekey(const void *name, int len) +{ + int64_t key; + + key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32; + if (key == 0) + key |= 0x100000000LL; + return(key); +} +#else +static int64_t +hammer_directory_namekey(const void *name __unused, int len __unused) +{ + return (0); +} +#endif + + +#ifndef BOOT2 +/* + * Misc + */ +static u_int32_t +hammer_to_unix_xid(uuid_t *uuid) +{ + return(*(u_int32_t *)&uuid->node[2]); +} + +static int +hammer_get_dtype(u_int8_t obj_type) +{ + switch(obj_type) { + case HAMMER_OBJTYPE_DIRECTORY: + return(DT_DIR); + case HAMMER_OBJTYPE_REGFILE: + return(DT_REG); + case HAMMER_OBJTYPE_DBFILE: + return(DT_DBF); + case HAMMER_OBJTYPE_FIFO: + return(DT_FIFO); + case HAMMER_OBJTYPE_SOCKET: + return(DT_SOCK); + case HAMMER_OBJTYPE_CDEV: + return(DT_CHR); + case HAMMER_OBJTYPE_BDEV: + return(DT_BLK); + case HAMMER_OBJTYPE_SOFTLINK: + return(DT_LNK); + default: + return(DT_UNKNOWN); + } + /* not reached */ +} + +static int +hammer_get_mode(u_int8_t obj_type) +{ + switch(obj_type) { + case HAMMER_OBJTYPE_DIRECTORY: + return(S_IFDIR); + case HAMMER_OBJTYPE_REGFILE: + return(S_IFREG); + case HAMMER_OBJTYPE_DBFILE: + return(S_IFDB); + case HAMMER_OBJTYPE_FIFO: + return(S_IFIFO); + case HAMMER_OBJTYPE_SOCKET: + return(S_IFSOCK); + case HAMMER_OBJTYPE_CDEV: + return(S_IFCHR); + case HAMMER_OBJTYPE_BDEV: + return(S_IFBLK); + case HAMMER_OBJTYPE_SOFTLINK: + return(S_IFLNK); + default: + return(0); + } + /* not reached */ +} + +#if DEBUG > 1 +static void +hprintb(hammer_base_elm_t e) +{ + printf("%d/", e->localization); + if (e->obj_id >> 32 != 0) + printf("%lx%08lx", + (long)(e->obj_id >> 32), + (long)(e->obj_id & 0xffffffff)); + else + printf("%lx", (long)e->obj_id); + printf("/%d/", e->rec_type); + if (e->key >> 32 != 0) + printf("%lx%08lx", + (long)(e->key >> 32), + (long)(e->key & 0xffffffff)); + else + printf("%lx", (long)e->key); +#ifdef TESTING + printf("/%llx/%llx", e->create_tid, e->delete_tid); +#endif +} +#endif /* DEBUG > 1 */ +#endif /* !BOOT2 */ + +static hammer_btree_leaf_elm_t +hfind(struct hfs *hfs, hammer_base_elm_t key, hammer_base_elm_t end) +{ +#if DEBUG > 1 + printf("searching for "); + hprintb(key); + printf(" end "); + hprintb(end); + printf("\n"); +#endif + + int n; + int r; + struct hammer_base_elm search = *key; + struct hammer_base_elm backtrack; + hammer_off_t nodeoff = hfs->root; + hammer_node_ondisk_t node; + hammer_btree_elm_t e = NULL; + int internal; + +loop: + node = hread(hfs, nodeoff); + if (node == NULL) + return (NULL); + internal = node->type == HAMMER_BTREE_TYPE_INTERNAL; + +#if DEBUG > 3 + for (int i = 0; i < node->count; i++) { + printf("E: "); + hprintb(&node->elms[i].base); + printf("\n"); + } + if (internal) { + printf("B: "); + hprintb(&node->elms[node->count].base); + printf("\n"); + } +#endif + + n = hammer_btree_search_node(&search, node); + + // In internal nodes, we cover the right boundary as well. + // If we hit it, we'll backtrack. + for (; n < node->count + internal; n++) { + e = &node->elms[n]; + r = hammer_btree_cmp(&search, &e->base); + + if (r < 0) + break; + } + + // unless we stopped right on the left side, we need to back off a bit + if (n > 0) + e = &node->elms[--n]; + +#if DEBUG > 2 + printf(" found: "); + hprintb(&e->base); + printf("\n"); +#endif + + if (internal) { + // If we hit the right boundary, backtrack to + // the next higher level. + if (n == node->count) + goto backtrack; + nodeoff = e->internal.subtree_offset; + backtrack = (e+1)->base; + goto loop; + } + + r = hammer_btree_cmp(key, &e->base); + // If we're more off than the createtid, take the next elem + if (r > 1) { + e++; + n++; + } + + // Skip deleted elements + while (n < node->count && e->base.delete_tid != 0) { + e++; + n++; + } + + // In the unfortunate event when there is no next + // element in this node, we repeat the search with + // a key beyond the right boundary + if (n == node->count) { +backtrack: + search = backtrack; + nodeoff = hfs->root; + +#if DEBUG > 2 + printf("hit right boundary (%d), resetting search to ", + node->count); + hprintb(&search); + printf("\n"); +#endif + goto loop; + } + +#if DEBUG > 1 + printf(" result: "); + hprintb(&e->base); + printf("\n"); +#endif + + if (end != NULL) + if (hammer_btree_cmp(end, &e->base) < -1) + goto fail; + + return (&e->leaf); + +fail: +#if DEBUG > 1 + printf(" fail.\n"); +#endif + return (NULL); +} + +/* + * Returns the directory entry localization field based on the directory + * inode's capabilities. + */ +static u_int32_t +hdirlocalization(struct hfs *hfs, ino_t ino) +{ + struct hammer_base_elm key; + + if (ino != hfs->last_dir_ino) { + bzero(&key, sizeof(key)); + key.obj_id = ino; + key.localization = HAMMER_LOCALIZE_INODE; + key.rec_type = HAMMER_RECTYPE_INODE; + hammer_btree_leaf_elm_t e; + hammer_data_ondisk_t ed; + + e = hfind(hfs, &key, &key); + if (e) { + ed = hread(hfs, e->data_offset); + if (ed) { + hfs->last_dir_ino = ino; + hfs->last_dir_cap_flags = ed->inode.cap_flags; + } else { + printf("hdirlocal: no inode data for %llx\n", + (long long)ino); + } + } else { + printf("hdirlocal: no inode entry for %llx\n", + (long long)ino); + } + } + if (hfs->last_dir_cap_flags & HAMMER_INODE_CAP_DIR_LOCAL_INO) + return(HAMMER_LOCALIZE_INODE); + else + return(HAMMER_LOCALIZE_MISC); +} + +#ifndef BOOT2 +static int +hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de) +{ + struct hammer_base_elm key, end; + +#if DEBUG > 2 + printf("%s(%llx, %lld)\n", __func__, (long long)ino, *off); +#endif + + bzero(&key, sizeof(key)); + key.obj_id = ino; + key.localization = hdirlocalization(hfs, ino); + key.rec_type = HAMMER_RECTYPE_DIRENTRY; + key.key = *off; + + end = key; + end.key = HAMMER_MAX_KEY; + + hammer_btree_leaf_elm_t e; + + e = hfind(hfs, &key, &end); + if (e == NULL) { + errno = ENOENT; + return (-1); + } + + *off = e->base.key + 1; // remember next pos + + de->d_namlen = e->data_len - HAMMER_ENTRY_NAME_OFF; + de->d_type = hammer_get_dtype(e->base.obj_type); + hammer_data_ondisk_t ed = hread(hfs, e->data_offset); + if (ed == NULL) + return (-1); + de->d_ino = ed->entry.obj_id; + bcopy(ed->entry.name, de->d_name, de->d_namlen); + de->d_name[de->d_namlen] = 0; + + return (0); +} +#endif + +static ino_t +hresolve(struct hfs *hfs, ino_t dirino, const char *name) +{ + struct hammer_base_elm key, end; + size_t namel = strlen(name); + +#if DEBUG > 2 + printf("%s(%llx, %s)\n", __func__, (long long)dirino, name); +#endif + + bzero(&key, sizeof(key)); + key.obj_id = dirino; + key.localization = hdirlocalization(hfs, dirino); + key.key = hammer_directory_namekey(name, namel); + key.rec_type = HAMMER_RECTYPE_DIRENTRY; + end = key; + end.key = HAMMER_MAX_KEY; + + hammer_btree_leaf_elm_t e; + while ((e = hfind(hfs, &key, &end)) != NULL) { + key.key = e->base.key + 1; + + size_t elen = e->data_len - HAMMER_ENTRY_NAME_OFF; + hammer_data_ondisk_t ed = hread(hfs, e->data_offset); + if (ed == NULL) + return (-1); +#ifdef BOOT2 + if (ls) { + for (int i = 0; i < elen; i++) + putchar(ed->entry.name[i]); + putchar(' '); + ls = 2; + continue; + } +#endif + if (elen == namel && memcmp(ed->entry.name, name, MIN(elen, namel)) == 0) + return (ed->entry.obj_id); + } + +#if BOOT2 + if (ls == 2) + printf("\n"); +#endif + + return -1; +} + +static ino_t +hlookup(struct hfs *hfs, const char *path) +{ +#if DEBUG > 2 + printf("%s(%s)\n", __func__, path); +#endif + +#ifdef BOOT2 + ls = 0; +#endif + ino_t ino = 1; + do { + char name[MAXPATHLEN + 1]; + while (*path == '/') + path++; + if (*path == 0) + break; + for (char *n = name; *path != 0 && *path != '/'; path++, n++) { + n[0] = *path; + n[1] = 0; + } + +#ifdef BOOT2 + // A single ? means "list" + if (name[0] == '?' && name[1] == 0) + ls = 1; +#endif + + ino = hresolve(hfs, ino, name); + } while (ino != (ino_t)-1 && *path != 0); + + return (ino); +} + + +#ifndef BOOT2 +static int +hstat(struct hfs *hfs, ino_t ino, struct stat* st) +{ + struct hammer_base_elm key; + +#if DEBUG > 2 + printf("%s(%llx)\n", __func__, (long long)ino); +#endif + + bzero(&key, sizeof(key)); + key.obj_id = ino; + key.localization = HAMMER_LOCALIZE_INODE; + key.rec_type = HAMMER_RECTYPE_INODE; + + hammer_btree_leaf_elm_t e = hfind(hfs, &key, &key); + if (e == NULL) { +#ifndef BOOT2 + errno = ENOENT; +#endif + return -1; + } + + hammer_data_ondisk_t ed = hread(hfs, e->data_offset); + if (ed == NULL) + return (-1); + + st->st_mode = ed->inode.mode | hammer_get_mode(ed->inode.obj_type); + st->st_uid = hammer_to_unix_xid(&ed->inode.uid); + st->st_gid = hammer_to_unix_xid(&ed->inode.gid); + st->st_size = ed->inode.size; + + return (0); +} +#endif + +static ssize_t +hreadf(struct hfs *hfs, ino_t ino, int64_t off, int64_t len, char *buf) +{ + int64_t startoff = off; + struct hammer_base_elm key, end; + + bzero(&key, sizeof(key)); + key.obj_id = ino; + key.localization = HAMMER_LOCALIZE_MISC; + key.rec_type = HAMMER_RECTYPE_DATA; + end = key; + end.key = HAMMER_MAX_KEY; + + while (len > 0) { + key.key = off + 1; + hammer_btree_leaf_elm_t e = hfind(hfs, &key, &end); + int64_t dlen; + + if (e == NULL || off > e->base.key) { + bzero(buf, len); + off += len; + len = 0; + break; + } + + int64_t doff = e->base.key - e->data_len; + if (off < doff) { + // sparse file, beginning + dlen = doff - off; + dlen = MIN(dlen, len); + bzero(buf, dlen); + } else { + int64_t boff = off - doff; + hammer_off_t roff = e->data_offset; + + dlen = e->data_len; + dlen -= boff; + dlen = MIN(dlen, len); + + while (boff >= HAMMER_BUFSIZE) { + boff -= HAMMER_BUFSIZE; + roff += HAMMER_BUFSIZE; + } + + /* + * boff - relative offset in disk buffer (not aligned) + * roff - base offset of disk buffer (not aligned) + * dlen - amount of data we think we can copy + * + * hread only reads 16K aligned buffers, check for + * a length overflow and truncate dlen appropriately. + */ + if ((roff & ~HAMMER_BUFMASK64) != ((roff + boff + dlen - 1) & ~HAMMER_BUFMASK64)) + dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK); + char *data = hread(hfs, roff); + if (data == NULL) + return (-1); + bcopy(data + boff, buf, dlen); + } + + buf += dlen; + off += dlen; + len -= dlen; + } + + return (off - startoff); +} + +#ifdef BOOT2 +struct hfs hfs; + +static int +boot2_hammer_init(void) +{ + hammer_volume_ondisk_t volhead; + + volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0)); + if (volhead == NULL) + return (-1); + if (volhead->vol_signature != HAMMER_FSBUF_VOLUME) + return (-1); + hfs.root = volhead->vol0_btree_root; + hfs.buf_beg = volhead->vol_buf_beg; + return (0); +} + +static boot2_ino_t +boot2_hammer_lookup(const char *path) +{ + ino_t ino = hlookup(&hfs, path); + + if (ino == -1) + ino = 0; + + fs_off = 0; + + return (ino); +} + +static ssize_t +boot2_hammer_read(boot2_ino_t ino, void *buf, size_t len) +{ + ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf); + if (rlen != -1) + fs_off += rlen; + return (rlen); +} + +const struct boot2_fsapi boot2_hammer_api = { + .fsinit = boot2_hammer_init, + .fslookup = boot2_hammer_lookup, + .fsread = boot2_hammer_read +}; + +#endif + +#ifndef BOOT2 +static int +hinit(struct hfs *hfs) +{ +#if DEBUG + printf("hinit\n"); +#endif + for (int i = 0; i < NUMCACHE; i++) { + hfs->cache[i].data = malloc(HAMMER_BUFSIZE); + hfs->cache[i].off = -1; // invalid + hfs->cache[i].use = 0; + +#if DEBUG + if (hfs->cache[i].data == NULL) + printf("malloc failed\n"); +#endif + } + hfs->lru = 0; + hfs->last_dir_ino = -1; + + hammer_volume_ondisk_t volhead = hread(hfs, HAMMER_ZONE_ENCODE(1, 0)); + +#ifdef TESTING + if (volhead) { + printf("signature: %svalid\n", + volhead->vol_signature != HAMMER_FSBUF_VOLUME ? + "in" : + ""); + printf("name: %s\n", volhead->vol_name); + } +#endif + + if (volhead == NULL || volhead->vol_signature != HAMMER_FSBUF_VOLUME) { + for (int i = 0; i < NUMCACHE; i++) { + free(hfs->cache[i].data); + hfs->cache[i].data = NULL; + } + errno = ENODEV; + return (-1); + } + + hfs->root = volhead->vol0_btree_root; + hfs->buf_beg = volhead->vol_buf_beg; + + return (0); +} + +static void +hclose(struct hfs *hfs) +{ +#if DEBUG + printf("hclose\n"); +#endif + for (int i = 0; i < NUMCACHE; i++) { + if (hfs->cache[i].data) { + free(hfs->cache[i].data); + hfs->cache[i].data = NULL; + } + } +} +#endif + +#ifdef LIBSTAND +struct hfile { + struct hfs hfs; + ino_t ino; + int64_t fsize; +}; + +static int +hammer_open(const char *path, struct open_file *f) +{ + struct hfile *hf = malloc(sizeof(*hf)); + + bzero(hf, sizeof(*hf)); + f->f_fsdata = hf; + hf->hfs.f = f; + f->f_offset = 0; + + int rv = hinit(&hf->hfs); + if (rv) { + f->f_fsdata = NULL; + free(hf); + return (rv); + } + +#if DEBUG + printf("hammer_open %s %p %ld\n", path, f); +#endif + + hf->ino = hlookup(&hf->hfs, path); + if (hf->ino == -1) + goto fail; + + struct stat st; + if (hstat(&hf->hfs, hf->ino, &st) == -1) + goto fail; + hf->fsize = st.st_size; + +#if DEBUG + printf(" %ld\n", (long)hf->fsize); +#endif + + return (0); + +fail: +#if DEBUG + printf("hammer_open fail\n"); +#endif + f->f_fsdata = NULL; + hclose(&hf->hfs); + free(hf); + return (ENOENT); +} + +static int +hammer_close(struct open_file *f) +{ + struct hfile *hf = f->f_fsdata; + + f->f_fsdata = NULL; + if (hf) { + hclose(&hf->hfs); + free(hf); + } + return (0); +} + +static int +hammer_read(struct open_file *f, void *buf, size_t len, size_t *resid) +{ + struct hfile *hf = f->f_fsdata; + +#if DEBUG + printf("hammer_read %p %ld %ld\n", f, f->f_offset, len); +#endif + + if (f->f_offset >= hf->fsize) + return (EINVAL); + + size_t maxlen = len; + if (f->f_offset + len > hf->fsize) + maxlen = hf->fsize - f->f_offset; + + ssize_t rlen = hreadf(&hf->hfs, hf->ino, f->f_offset, maxlen, buf); + if (rlen == -1) + return (EINVAL); + + f->f_offset += rlen; + + *resid = len - rlen; + return (0); +} + +static off_t +hammer_seek(struct open_file *f, off_t offset, int whence) +{ + struct hfile *hf = f->f_fsdata; + + switch (whence) { + case SEEK_SET: + f->f_offset = offset; + break; + case SEEK_CUR: + f->f_offset += offset; + break; + case SEEK_END: + f->f_offset = hf->fsize - offset; + break; + default: + return (-1); + } + return (f->f_offset); +} + +static int +hammer_stat(struct open_file *f, struct stat *st) +{ + struct hfile *hf = f->f_fsdata; + + return (hstat(&hf->hfs, hf->ino, st)); +} + +static int +hammer_readdir(struct open_file *f, struct dirent *d) +{ + struct hfile *hf = f->f_fsdata; + + int64_t off = f->f_offset; + int rv = hreaddir(&hf->hfs, hf->ino, &off, d); + f->f_offset = off; + return (rv); +} + +// libstand +struct fs_ops hammer_fsops = { + "hammer", + hammer_open, + hammer_close, + hammer_read, + null_write, + hammer_seek, + hammer_stat, + hammer_readdir +}; +#endif // LIBSTAND + +#ifdef TESTING +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "usage: hammerread \n"); + return (1); + } + + struct hfs hfs; + hfs.fd = open(argv[1], O_RDONLY); + if (hfs.fd == -1) + err(1, "unable to open %s", argv[1]); + + if (hinit(&hfs) == -1) + err(1, "invalid hammerfs"); + + for (int i = 2; i < argc; i++) { + ino_t ino = hlookup(&hfs, argv[i]); + if (ino == (ino_t)-1) { + warn("hlookup %s", argv[i]); + continue; + } + + struct stat st; + if (hstat(&hfs, ino, &st)) { + warn("hstat %s", argv[i]); + continue; + } + + printf("%s %d/%d %o %lld\n", + argv[i], + st.st_uid, st.st_gid, + st.st_mode, st.st_size); + + if (S_ISDIR(st.st_mode)) { + int64_t off = 0; + struct dirent de; + while (hreaddir(&hfs, ino, &off, &de) == 0) { + printf("%s %d %llx\n", + de.d_name, de.d_type, de.d_ino); + } + } else if (S_ISREG(st.st_mode)) { + char *buf = malloc(100000); + int64_t off = 0; + while (off < st.st_size) { + int64_t len = MIN(100000, st.st_size - off); + int64_t rl = hreadf(&hfs, ino, off, len, buf); + fwrite(buf, rl, 1, stdout); + off += rl; + } + free(buf); + } + } + + return 0; +} +#endif diff --git a/lib/libstand/hammer2.c b/lib/libstand/hammer2.c index 0d4cf4ee60..665f1b4873 100644 --- a/lib/libstand/hammer2.c +++ b/lib/libstand/hammer2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012 The DragonFly Project. All rights reserved. + * Copyright (c) 2011-2013 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon @@ -32,10 +32,19 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + +#if !defined(BOOT2) && !defined(TESTING) +#define LIBSTAND 1 +#endif + +#ifdef BOOT2 +#include "boot2.h" +#endif + +#ifdef TESTING #include #include #include - #include #include #include @@ -45,133 +54,815 @@ #include #include #include +#endif + +#ifdef LIBSTAND +#include "stand.h" +#endif + +#include -#include +uint32_t iscsi_crc32(const void *buf, size_t size); +uint32_t iscsi_crc32_ext(const void *buf, size_t size, uint32_t ocrc); -struct hammer2 { - int fd; /* Device fd */ - struct hammer2_blockref sroot; /* Superroot blockref */ +#define hammer2_icrc32(buf, size) iscsi_crc32(buf, size) + +struct hammer2_fs { + struct hammer2_volume_data vol; + hammer2_blockref_t sroot; +#if defined(TESTING) + int fd; +#elif defined(LIBSTAND) + struct open_file *f; +#elif defined(BOOT2) + /* BOOT2 doesn't use a descriptor */ +#else +#error "hammer2: unknown library API" +#endif }; -struct inode { - struct hammer2_inode_data dat; /* raw inode data */ +struct hammer2_inode { + struct hammer2_inode_data ino; /* raw inode data */ off_t doff; /* disk inode offset */ }; -off_t blockoff(ref) - struct hammer2_blockref ref; +#ifdef BOOT2 + +static void +bzero(void *buf, size_t size) { + for (size_t i = 0; i < size; i++) + ((char *)buf)[i] = 0; +} +static void +bcopy(void *src, void *dst, size_t size) +{ + memcpy(dst, src, size); } -hinit(hfs) - struct hammer2 *hfs; +#if 0 +static size_t +strlen(const char *s) { - struct hammer2_volume_data volhdr; - ssize_t rc; - hammer2_crc_t crc0; + size_t l = 0; + for (; *s != 0; s++) + l++; + return (l); +} +#endif - rc = pread(hfs->fd, &volhdr, HAMMER2_VOLUME_SIZE, 0); - if (volhdr.magic == HAMMER2_VOLUME_ID_HBO) { - printf("Valid HAMMER2 filesystem\n"); - } else { - return (-1); +static int +memcmp(const void *a, const void *b, size_t len) +{ + for (size_t p = 0; p < len; p++) { + int r = ((const char *)a)[p] - ((const char *)b)[p]; + if (r != 0) + return (r); } - hfs->sroot = volhdr.sroot_blockref; return (0); } -shread(hfs, ino, buf, off, len) - struct hammer2 *hfs; - struct inode *ino; - char *buf; - off_t off; - size_t len; +#endif + +static +off_t +blockoff(hammer2_blockref_t *bref) +{ + return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX); +} + +static +size_t +blocksize(hammer2_blockref_t *bref) +{ + return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX)); +} + +static +hammer2_key_t +hammer2_dirhash(const unsigned char *name, size_t len) { + const unsigned char *aname = name; + uint32_t crcx; + uint64_t key; + size_t i; + size_t j; + + key = 0; + + /* + * m32 + */ + crcx = 0; + for (i = j = 0; i < len; ++i) { + if (aname[i] == '.' || + aname[i] == '-' || + aname[i] == '_' || + aname[i] == '~') { + if (i != j) + crcx += hammer2_icrc32(aname + j, i - j); + j = i + 1; + } + } + if (i != j) + crcx += hammer2_icrc32(aname + j, i - j); + + /* + * The directory hash utilizes the top 32 bits of the 64-bit key. + * Bit 63 must be set to 1. + */ + crcx |= 0x80000000U; + key |= (uint64_t)crcx << 32; + /* - * Read [off, off+len) from inode ino rather than from disk - * offsets; correctly decodes blockrefs/indirs/... + * l16 - crc of entire filename + * + * This crc reduces degenerate hash collision conditions */ + crcx = hammer2_icrc32(aname, len); + crcx = crcx ^ (crcx << 16); + key |= crcx & 0xFFFF0000U; + + /* + * Set bit 15. This allows readdir to strip bit 63 so a positive + * 64-bit cookie/offset can always be returned, and still guarantee + * that the values 0x0000-0x7FFF are available for artificial entries. + * ('.' and '..'). + */ + key |= 0x8000U; + + return (key); } -struct inode *hlookup1(hfs, ino, name) - struct hammer2 *hfs; - struct inode *ino; - char *name; +/* + * Low level read + */ +static +int +h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off) { - static struct inode filino; - off_t off; +#if defined(LIBSTAND) + size_t rlen; +#endif + int rc; + +#if defined(TESTING) + rc = pread(hfs->fd, &hfs->vol, nbytes, off); + if (rc == (int)nbytes) + rc = 0; + else + rc = -1; +#elif defined(LIBSTAND) + rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ, + off >> DEV_BSHIFT, nbytes, + buf, &rlen); + if (rc || rlen != nbytes) + rc = -1; +#elif defined(BOOT2) + rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT); + if (rc) + rc = -1; +#else +#error "hammer2: unknown library API" +#endif + return rc; +} + +/* + * Common code + * + * Initialize for HAMMER2 filesystem access given a hammer2_fs with + * its device file descriptor initialized. + */ + +/* + * Lookup within the block specified by (*base), loading the block from disk + * if necessary. Locate the first key within the requested range and + * recursively run through indirect blocks as needed. The caller may loop + * by setting key_beg to *key_ret. + * + * Returns 0 if nothing could be found and the key range has been exhausted. + * returns -1 if a disk error occured. Otherwise returns the size of the + * data block and returns the data block in *pptr and bref in *bref_ret. + * + * NOTE! When reading file data, the returned bref's key will be the nearest + * data block we looked up. The file read procedure must handle any + * zero-fill or skip. However, we will truncate the return value to + * the file size. + */ +static int +h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base, + hammer2_key_t key_beg, hammer2_key_t key_end, + hammer2_blockref_t *bref_ret, void **pptr) +{ + hammer2_blockref_t *bref; + hammer2_blockref_t best; + hammer2_key_t scan_beg; + hammer2_key_t scan_end; + int i; int rc; + int count; + int dev_boff; + int dev_bsize; + static hammer2_media_data_t media; + static hammer2_blockref_t saved_base; - bzero(&filino, sizeof(struct inode)); + if (base == NULL) { + saved_base.data_off = (hammer2_off_t)-1; + return(0); + } + if (base->data_off == (hammer2_off_t)-1) + return(-1); - for (off = 0; - off < ino->dat.size; - off += sizeof(struct hammer2_inode_data)) - { - rc = shread(hfs, ino, &filino.dat, off, - sizeof(struct hammer2_inode_data)); - if (rc != sizeof(struct hammer2_inode_data)) + /* + * Calculate the number of blockrefs to scan + */ + switch(base->type) { + case HAMMER2_BREF_TYPE_VOLUME: + count = HAMMER2_SET_COUNT; + break; + case HAMMER2_BREF_TYPE_INODE: + count = HAMMER2_SET_COUNT; + break; + case HAMMER2_BREF_TYPE_INDIRECT: + count = blocksize(base) / sizeof(hammer2_blockref_t); + break; + } + + /* + * Find the best candidate (the lowest blockref within the specified + * range). The array can be fully set associative so make no ordering + * assumptions. + */ +again: + best.key = HAMMER2_MAX_KEY; + best.type = 0; + + for (i = 0; i < count; ++i) { + /* + * [re]load when returning from our recursion + */ + if (base->type != HAMMER2_BREF_TYPE_VOLUME && + base->data_off != saved_base.data_off) { + if (h2read(hfs, &media, + blocksize(base), blockoff(base))) { + return(-1); + } + saved_base = *base; + } + + /* + * Special case embedded file data + */ + if (base->type == HAMMER2_BREF_TYPE_INODE) { + if (media.ipdata.op_flags & HAMMER2_OPFLAG_DIRECTDATA) { + *pptr = media.ipdata.u.data; + bref_ret->type = HAMMER2_BREF_TYPE_DATA; + bref_ret->key = 0; + return HAMMER2_EMBEDDED_BYTES; + } + } + + /* + * Calculate the bref in our scan. + */ + switch(base->type) { + case HAMMER2_BREF_TYPE_VOLUME: + bref = &hfs->vol.sroot_blockset.blockref[i]; + break; + case HAMMER2_BREF_TYPE_INODE: + bref = &media.ipdata.u.blockset.blockref[i]; + break; + case HAMMER2_BREF_TYPE_INDIRECT: + bref = &media.npdata[i]; + break; + } + if (bref->type == 0) continue; - if (strcmp(name, &filino.dat.filename) == 0) - return (&filino); + if (bref->key > best.key) + continue; + scan_beg = bref->key; + scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1; + if (scan_end >= key_beg && scan_beg <= key_end) { + best = *bref; + } } - return (NULL); + /* + * Figure out what to do with the results. + */ + switch(best.type) { + case 0: + /* + * Return 0 + */ + rc = 0; + break; + case HAMMER2_BREF_TYPE_INDIRECT: + /* + * Matched an indirect block. If the block turns out to + * contain nothing we continue the iteration, otherwise + * we return the data from the recursion. + * + * Be sure to handle the overflow case when recalculating + * key_beg. + */ + rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr); + if (rc == 0) { + key_beg = best.key + + ((hammer2_key_t)1 << best.keybits); + if (key_beg > best.key && key_beg <= key_end) + goto again; + } + break; + case HAMMER2_BREF_TYPE_INODE: + case HAMMER2_BREF_TYPE_DATA: + /* + * Terminal match. Leaf elements might not be data-aligned. + */ + dev_bsize = blocksize(&best); + if (dev_bsize < HAMMER2_LBUFSIZE) + dev_bsize = HAMMER2_LBUFSIZE; + dev_boff = blockoff(&best) - + (blockoff(&best) & ~HAMMER2_LBUFMASK64); + if (h2read(hfs, &media, dev_bsize, blockoff(&best) - dev_boff)) + return(-1); + saved_base.data_off = (hammer2_off_t)-1; + *bref_ret = best; + *pptr = media.buf + dev_boff; + rc = blocksize(&best); + break; + } + return(rc); } -struct inode *hlookup(hfs, name) - struct hammer2 *hfs; - char *name; +static +void +h2resolve(struct hammer2_fs *hfs, const char *path, + hammer2_blockref_t *bref, hammer2_inode_data_t **inop) { - /* Name is of form /SUPERROOT/a/b/c/file */ + hammer2_blockref_t bres; + hammer2_inode_data_t *ino; + hammer2_key_t key; + ssize_t bytes; + size_t len; + + /* + * Start point (superroot) + */ + *bref = hfs->sroot; + if (inop) + *inop = NULL; + + /* + * Iterate path elements + */ + while (*path) { + while (*path == '/') + ++path; + if (*path == 0) /* terminal */ + break; + /* + * Calculate path element and look for it in the directory + */ + for (len = 0; path[len]; ++len) { + if (path[len] == '/') + break; + } + key = hammer2_dirhash(path, len); + for (;;) { + bytes = h2lookup(hfs, bref, + key, key | 0xFFFFU, + &bres, (void **)&ino); + if (bytes == 0) + break; + if (len == ino->name_len && + memcmp(path, ino->filename, len) == 0) { + if (inop) + *inop = ino; + break; + } + key = bres.key + 1; + } + + /* + * Lookup failure + */ + if (bytes == 0) { + bref->data_off = (hammer2_off_t)-1; + break; + } + + /* + * Check path continuance, inode must be a directory or + * we fail. + */ + path += len; + if (*path && ino->type != HAMMER2_OBJTYPE_DIRECTORY) { + bref->data_off = (hammer2_off_t)-1; + break; + } + *bref = bres; + } } -void hstat(hfs, ino, sb) - struct hammer2 *hfs; - struct inode *ino; - struct stat *sb; +static +ssize_t +h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref, + off_t off, off_t filesize, void *buf, size_t len) { + hammer2_blockref_t bres; + ssize_t total; + ssize_t bytes; + ssize_t zfill; + char *data; + + /* + * EOF edge cases + */ + if (off >= filesize) + return (0); + if (off + len > filesize) + len = filesize - off; + + /* + * Loop until done + */ + total = 0; + while (len) { + /* + * Find closest bres >= requested offset. + */ + bytes = h2lookup(hfs, bref, off, off + len - 1, + &bres, (void **)&data); + + if (bytes < 0) { + if (total == 0) + total = -1; + break; + } + + /* + * Load the data into the buffer. First handle a degenerate + * zero-fill case. + */ + if (bytes == 0) { + bzero(buf, len); + total += len; + break; + } + + /* + * Returned record overlaps to the left of the requested + * position. It must overlap in this case or h2lookup() + * would have returned something else. + */ + if (bres.key < off) { + data += off - bres.key; + bytes -= off - bres.key; + } + + /* + * Returned record overlaps to the right of the requested + * position, handle zero-fill. Again h2lookup() only returns + * this case if there is an actual overlap. + */ + if (bres.key > off) { + zfill = (ssize_t)(bres.key - off); + bzero(buf, zfill); + len -= zfill; + off += zfill; + total += zfill; + buf = (char *)buf + zfill; + } + /* + * Trim returned request before copying. + */ + if (bytes > len) + bytes = len; + bcopy(data, buf, bytes); + len -= bytes; + off += bytes; + total += bytes; + buf = (char *)buf + bytes; + } + return (total); } -main(argc, argv) - int argc; - char *argv[]; +static +int +h2init(struct hammer2_fs *hfs) { - struct hammer2 hammer2; - struct inode *ino; - struct stat sb; +#if 0 + uint32_t crc0; +#endif + hammer2_tid_t best_tid = 0; + void *data; + off_t off; + int best; int i; - if (argc < 2) { - fprintf(stderr, "usage: hammer2 \n"); - exit(1); + /* + * Find the best volume header + */ + best = -1; + for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { + off = i * HAMMER2_ZONE_BYTES64; + if (i) + no_io_error = 1; + if (h2read(hfs, &hfs->vol, sizeof(hfs->vol), off)) + continue; + if (hfs->vol.magic != HAMMER2_VOLUME_ID_HBO) + continue; + if (best < 0 || best_tid < hfs->vol.mirror_tid) { + best = i; + best_tid = hfs->vol.mirror_tid; + } + } + no_io_error = 0; + if (best < 0) + return(-1); + + /* + * Reload the best volume header and set up the blockref. + */ + off = best * HAMMER2_ZONE_BYTES64; + if (h2read(hfs, &hfs->vol, sizeof(hfs->vol), off)) + return(-1); + hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME; + hfs->sroot.data_off = off; + h2lookup(hfs, NULL, 0, 0, NULL, NULL); + h2lookup(hfs, &hfs->sroot, 0, 0, &hfs->sroot, &data); + + /* + * Clear cache + */ + h2lookup(hfs, NULL, 0, 0, NULL, NULL); + + return (0); +} + +/************************************************************************ + * BOOT2 SUPPORT * + ************************************************************************ + * + */ +#ifdef BOOT2 + +static struct hammer2_fs hfs; + +static int +boot2_hammer2_init(void) +{ + if (h2init(&hfs)) + return(-1); + return(0); +} + +static boot2_ino_t +boot2_hammer2_lookup(const char *path) +{ + hammer2_blockref_t bref; + + h2resolve(&hfs, path, &bref, NULL); + return ((boot2_ino_t)bref.data_off); +} + +static ssize_t +boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len) +{ + hammer2_blockref_t bref; + ssize_t total; + + bzero(&bref, sizeof(bref)); + bref.type = HAMMER2_BREF_TYPE_INODE; + bref.data_off = ino; + + total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len); + if (total > 0) + fs_off += total; + return total; +} + +const struct boot2_fsapi boot2_hammer2_api = { + .fsinit = boot2_hammer2_init, + .fslookup = boot2_hammer2_lookup, + .fsread = boot2_hammer2_read +}; + +#endif + +/************************************************************************ + * BOOT2 SUPPORT * + ************************************************************************ + * + */ +#ifdef LIBSTAND + +struct hfile { + struct hammer2_fs hfs; + hammer2_blockref_t bref; + int64_t fsize; + uint32_t mode; + uint8_t type; +}; + +static +int +hammer2_get_dtype(uint8_t type) +{ + switch(type) { + case HAMMER2_OBJTYPE_DIRECTORY: + return(DT_DIR); + case HAMMER2_OBJTYPE_REGFILE: + return(DT_REG); + case HAMMER2_OBJTYPE_FIFO: + return(DT_FIFO); + case HAMMER2_OBJTYPE_CDEV: + return(DT_CHR); + case HAMMER2_OBJTYPE_BDEV: + return(DT_BLK); + case HAMMER2_OBJTYPE_SOFTLINK: + return(DT_LNK); + case HAMMER2_OBJTYPE_HARDLINK: + return(DT_UNKNOWN); + case HAMMER2_OBJTYPE_SOCKET: + return(DT_SOCK); + default: + return(DT_UNKNOWN); } +} - hammer2.fd = open(argv[1], O_RDONLY); - if (hammer2.fd < 0) { - fprintf(stderr, "unable to open %s\n", argv[1]); - exit(1); +static +mode_t +hammer2_get_mode(uint8_t type) +{ + switch(type) { + case HAMMER2_OBJTYPE_DIRECTORY: + return(S_IFDIR); + case HAMMER2_OBJTYPE_REGFILE: + return(S_IFREG); + case HAMMER2_OBJTYPE_FIFO: + return(S_IFIFO); + case HAMMER2_OBJTYPE_CDEV: + return(S_IFCHR); + case HAMMER2_OBJTYPE_BDEV: + return(S_IFBLK); + case HAMMER2_OBJTYPE_SOFTLINK: + return(S_IFLNK); + case HAMMER2_OBJTYPE_HARDLINK: + return(0); + case HAMMER2_OBJTYPE_SOCKET: + return(S_IFSOCK); + default: + return(0); } +} + +static int +hammer2_open(const char *path, struct open_file *f) +{ + struct hfile *hf = malloc(sizeof(*hf)); + hammer2_inode_data_t *ipdata; + + bzero(hf, sizeof(*hf)); + f->f_offset = 0; + f->f_fsdata = hf; + hf->hfs.f = f; - if (hinit(&hammer2)) { - fprintf(stderr, "invalid fs\n"); - close(hammer2.fd); - exit(1); + if (h2init(&hf->hfs)) { + f->f_fsdata = NULL; + free(hf); + errno = ENOENT; + return(-1); } + h2resolve(&hf->hfs, path, &hf->bref, &ipdata); + if (hf->bref.data_off == (hammer2_off_t)-1 || + (hf->bref.type != HAMMER2_BREF_TYPE_INODE && + hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) { + f->f_fsdata = NULL; + free(hf); + errno = ENOENT; + return(-1); + } + if (ipdata) { + hf->fsize = ipdata->size; + hf->type = ipdata->type; + hf->mode = ipdata->mode | hammer2_get_mode(ipdata->type); + } else { + hf->fsize = 0; + hf->type = HAMMER2_OBJTYPE_DIRECTORY; + hf->mode = 0755 | S_IFDIR; + } + return(0); +} - for (i = 2; i < argc; i++) { - ino = hlookup(&hammer2, argv[i]); - if (ino == NULL) { - fprintf(stderr, "hlookup %s\n", argv[i]); - continue; - } - hstat(&hammer2, ino, &sb); +static int +hammer2_close(struct open_file *f) +{ + struct hfile *hf = f->f_fsdata; + + f->f_fsdata = NULL; + if (hf) + free(hf); + return (0); +} + +static int +hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid) +{ + struct hfile *hf = f->f_fsdata; + ssize_t total; + int rc = 0; + + total = h2readfile(&hf->hfs, &hf->bref, + f->f_offset, hf->fsize, buf, len); + if (total < 0) { + rc = EIO; + total = 0; + } else { + f->f_offset += total; + rc = 0; + } + *resid = len - total; + return rc; +} - printf("%s %lld", argv[i], sb.st_size); +static off_t +hammer2_seek(struct open_file *f, off_t offset, int whence) +{ + struct hfile *hf = f->f_fsdata; + switch (whence) { + case SEEK_SET: + f->f_offset = offset; + break; + case SEEK_CUR: + f->f_offset += offset; + break; + case SEEK_END: + f->f_offset = hf->fsize - offset; + break; + default: + return (-1); } + return (f->f_offset); } + +static int +hammer2_stat(struct open_file *f, struct stat *st) +{ + struct hfile *hf = f->f_fsdata; + + st->st_mode = hf->mode; + st->st_uid = 0; + st->st_gid = 0; + st->st_size = hf->fsize; + + return (0); +} + +static int +hammer2_readdir(struct open_file *f, struct dirent *den) +{ + struct hfile *hf = f->f_fsdata; + hammer2_blockref_t bres; + hammer2_inode_data_t *ipdata; + int bytes; + + for (;;) { + bytes = h2lookup(&hf->hfs, &hf->bref, + f->f_offset | HAMMER2_DIRHASH_VISIBLE, + HAMMER2_MAX_KEY, + &bres, (void **)&ipdata); + if (bytes <= 0) + break; + den->d_namlen = ipdata->name_len; + den->d_type = hammer2_get_dtype(ipdata->type); + den->d_ino = ipdata->inum; + bcopy(ipdata->filename, den->d_name, den->d_namlen); + den->d_name[den->d_namlen] = 0; + + f->f_offset = bres.key + 1; + + return(0); + } + return ENOENT; +} + +struct fs_ops hammer_fsops = { + "hammer2", + hammer2_open, + hammer2_close, + hammer2_read, + null_write, + hammer2_seek, + hammer2_stat, + hammer2_readdir +}; + +#endif diff --git a/lib/libstand/printf.c b/lib/libstand/printf.c index d7d67bb96d..44685f6a46 100644 --- a/lib/libstand/printf.c +++ b/lib/libstand/printf.c @@ -233,6 +233,7 @@ kvprintf(char const *fmt, void (*func)(int, void *), void *arg, int radix, qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; sign = 0; dot = 0; dwidth = 0; upper = 0; cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; + reswitch: switch (ch = (u_char)*fmt++) { case '.': dot = 1; diff --git a/lib/libstand/read.c b/lib/libstand/read.c index 337b0b5a19..4b9090e924 100644 --- a/lib/libstand/read.c +++ b/lib/libstand/read.c @@ -65,6 +65,8 @@ #include #include "stand.h" +int no_io_error; + ssize_t read(int fd, void *dest, size_t bcount) { diff --git a/lib/libstand/stand.h b/lib/libstand/stand.h index 150c9273b6..c5aee51d1e 100644 --- a/lib/libstand/stand.h +++ b/lib/libstand/stand.h @@ -152,6 +152,7 @@ struct devsw { extern struct devsw netdev; extern int errno; +extern int no_io_error; struct open_file { int f_flags; /* see F_* below */ diff --git a/sbin/newfs_hammer2/newfs_hammer2.c b/sbin/newfs_hammer2/newfs_hammer2.c index 9eeae7d26f..6ea0b074a0 100644 --- a/sbin/newfs_hammer2/newfs_hammer2.c +++ b/sbin/newfs_hammer2/newfs_hammer2.c @@ -522,8 +522,14 @@ format_hammer2(int fd, hammer2_off_t total_space, hammer2_off_t free_space) /* * Compression mode and supported copyids. + * + * Do not allow compression when creating a "boot" label + * (pfs-create also does the same if the pfs is named "boot") */ - rawip->comp_algo = HAMMER2_COMP_NEWFS_DEFAULT; + if (strcmp(Label, "boot") == 0) + rawip->comp_algo = HAMMER2_COMP_AUTOZERO; + else + rawip->comp_algo = HAMMER2_COMP_NEWFS_DEFAULT; rawip->pfs_clid = Hammer2_RPFSId; rawip->pfs_type = HAMMER2_PFSTYPE_MASTER; diff --git a/sys/boot/Makefile b/sys/boot/Makefile index 783c5e4072..a800d04e08 100644 --- a/sys/boot/Makefile +++ b/sys/boot/Makefile @@ -14,7 +14,11 @@ SUBDIR+= ofw .endif # Pick the machine-dependant subdir based on the target architecture. +.if ${REALLY_X86_64} == "true" +SUBDIR+= pc32 +.else SUBDIR+= ${MACHINE_PLATFORM} +.endif # Build EFI executable on ia64 .if ${MACHINE_ARCH} == "ia64" diff --git a/sys/boot/common/boot2.h b/sys/boot/common/boot2.h index 262869b76e..89ab80e388 100644 --- a/sys/boot/common/boot2.h +++ b/sys/boot/common/boot2.h @@ -33,7 +33,7 @@ */ /* * boot2 encapsulated ABI. The boot2 standalone code provides these functions - * to the boot2 add-on modules (ufsread.c and hammerread.c). + * to the boot2 add-on modules (ufsread.c and hammer2.c). */ #ifndef _BOOT_COMMON_BOOT2_H_ @@ -53,7 +53,7 @@ * We only need a 32 bit ino_t for UFS-only boot code. We have to squeeze * space usage, else we'd just use 64 bits across the board. */ -#if defined(HAMMERFS) +#if defined(HAMMERFS) || defined(HAMMER2FS) typedef uint64_t boot2_ino_t; #else typedef uint32_t boot2_ino_t; @@ -75,6 +75,7 @@ struct boot2_dmadat { }; extern uint32_t fs_off; +extern int no_io_error; extern int ls; extern struct boot2_dmadat *boot2_dmadat; @@ -90,5 +91,8 @@ extern const struct boot2_fsapi boot2_ufs_api; #ifdef HAMMERFS extern const struct boot2_fsapi boot2_hammer_api; #endif +#ifdef HAMMER2FS +extern const struct boot2_fsapi boot2_hammer2_api; +#endif #endif diff --git a/sys/boot/common/help.common b/sys/boot/common/help.common index 724989f296..935371a3ea 100644 --- a/sys/boot/common/help.common +++ b/sys/boot/common/help.common @@ -505,13 +505,13 @@ "hammer:da8se1a" - One file system HAMMER multi volume examples: + One file system HAMMER2 redundant copies examples: - "hammer:da8s1a:da9s1a" + "hammer2:da8s1a:da9s1a" Several file systems, boot list, example: - "ufs:da0s1a;hammer:ad1s1d" + "ufs:da0s1a;hammer2:ad1s1d" Each file system in the list will be tried in the order specified until the mount succeeds. If all fail, the diff --git a/sys/boot/common/loader.8 b/sys/boot/common/loader.8 index 60c09d6570..83d545c40e 100644 --- a/sys/boot/common/loader.8 +++ b/sys/boot/common/loader.8 @@ -749,12 +749,12 @@ One file system example: .Dl hammer:da8s1a .Pp One file system -.Nm HAMMER +.Nm HAMMER2 multi volume example: -.Dl hammer:da8s1a:da9s1a +.Dl hammer2:da8s1a:da9s1a .Pp Several file systems, boot list, example: -.Dl ufs:da0s1a;hammer:ad1s1d +.Dl ufs:da0s1a;hammer2:ad1s1d .Pp Each file system in the list will be tried in the order specified until the mount succeeds. diff --git a/sys/boot/pc32/boot2/Makefile b/sys/boot/pc32/boot2/Makefile index 9e4e47a374..6df6ac3443 100644 --- a/sys/boot/pc32/boot2/Makefile +++ b/sys/boot/pc32/boot2/Makefile @@ -14,6 +14,7 @@ NXLDFLAGS= .PATH: ${.CURDIR}/.. .PATH: ${.CURDIR}/../../common .PATH: ${.CURDIR}/../../../../lib/libstand +.PATH: ${.CURDIR}/../../../libkern # A value of 0x80 enables LBA support. B1FLAGS= 0x80 @@ -90,8 +91,8 @@ _ADDCFLAGS$s:= -DNSECT=${NSECT$s} _ts= $s .if ${_ts} == "_64" _ADDCFLAGS$s+= -DDISKLABEL64 -_ADDCFLAGS$s+= -DHAMMERFS -DUFS -_ADDOBJS$s+= hammerread$s.o ufsread$s.o +_ADDCFLAGS$s+= -DHAMMER2FS -DUFS +_ADDOBJS$s+= hammer2$s.o ufsread$s.o icrc32$s.o .else _ADDCFLAGS$s+= -DUFS -DUFS1_ONLY _ADDOBJS$s+= ufsread$s.o @@ -109,7 +110,9 @@ boot1$s.o: boot1.S ${CC} ${CFLAGS} ${_ADDCFLAGS$s} -DFLAGS=${B1FLAGS} \ ${.ALLSRC} -o ${.TARGET} -c -boot2$s.s: boot2.c boot2$s.h ${.CURDIR}/../../common/ufsread.c ${.CURDIR}/../../../../lib/libstand/hammerread.c +#${.CURDIR}/../../common/ufsread.c ${.CURDIR}/../../../../lib/libstand/hammer2.c + +boot2$s.s: boot2.c boot2$s.h ufsread.c hammer2.c icrc32.c ${CC} ${CFLAGS} ${_ADDCFLAGS$s} -S -o boot2$s.s.tmp ${.CURDIR}/boot2.c sed -e '/align/d' -e '/nop/d' < ${.TARGET}.tmp > ${.TARGET} rm -f ${.TARGET}.tmp @@ -146,12 +149,15 @@ CLEANFILES+= boot1$s boot1$s.out boot1$s.o \ boot2$s.ld boot2$s.out boot2$s.o boot2$s.h boot2$s.s \ ${_ADDOBJS$s} -hammerread$s.o: hammerread.c +hammer2$s.o: hammer2.c ${CC} ${CFLAGS} ${_ADDCFLAGS$s} ${.ALLSRC} -o ${.TARGET} -c ufsread$s.o: ufsread.c ${CC} ${CFLAGS} ${_ADDCFLAGS$s} ${.ALLSRC} -o ${.TARGET} -c +icrc32$s.o: icrc32.c + ${CC} ${CFLAGS} ${_ADDCFLAGS$s} ${.ALLSRC} -o ${.TARGET} -c + .endfor sio.o: sio.S diff --git a/sys/boot/pc32/boot2/boot2.c b/sys/boot/pc32/boot2/boot2.c index 5d0df04f37..1ea9632f76 100644 --- a/sys/boot/pc32/boot2/boot2.c +++ b/sys/boot/pc32/boot2/boot2.c @@ -168,6 +168,7 @@ static struct bootinfo bootinfo; * NOTE: boot2_dmadat is extended by per-filesystem APIs */ uint32_t fs_off; +int no_io_error; int ls; struct boot2_dmadat *boot2_dmadat; @@ -200,7 +201,7 @@ strcmp(const char *s1, const char *s2) return ((int)((unsigned char)*s1 - (unsigned char)*s2)); } -#if defined(UFS) && defined(HAMMERFS) +#if defined(UFS) && defined(HAMMER2FS) const struct boot2_fsapi *fsapi; @@ -208,9 +209,9 @@ const struct boot2_fsapi *fsapi; #define fsapi (&boot2_ufs_api) -#elif defined(HAMMERFS) +#elif defined(HAMMER2FS) -#define fsapi (&boot2_hammer_api) +#define fsapi (&boot2_hammer2_api) #endif @@ -602,11 +603,11 @@ dskprobe(void) /* * Probe filesystem */ -#if defined(UFS) && defined(HAMMERFS) +#if defined(UFS) && defined(HAMMER2FS) if (boot2_ufs_api.fsinit() == 0) { fsapi = &boot2_ufs_api; - } else if (boot2_hammer_api.fsinit() == 0) { - fsapi = &boot2_hammer_api; + } else if (boot2_hammer2_api.fsinit() == 0) { + fsapi = &boot2_hammer2_api; } else { printf("fs probe failed\n"); fsapi = &boot2_ufs_api; @@ -639,6 +640,9 @@ printf(const char *fmt,...) static char buf[10]; char *s; unsigned u; +#ifdef HAMMER2FS + uint64_t q; +#endif int c; va_start(ap, fmt); @@ -653,6 +657,33 @@ printf(const char *fmt,...) for (s = va_arg(ap, char *); *s; s++) putchar(*s); continue; +#ifdef HAMMER2FS + case 'q': + ++fmt; /* skip the 'x' */ + q = va_arg(ap, uint64_t); + s = buf; + do { + if ((q & 15) < 10) + *s++ = '0' + (q & 15); + else + *s++ = 'a' + (q & 15) - 10; + } while (q >>= 4); + while (--s >= buf) + putchar(*s); + continue; + case 'x': + u = va_arg(ap, unsigned); + s = buf; + do { + if ((u & 15) < 10) + *s++ = '0' + (u & 15); + else + *s++ = 'a' + (u & 15) - 10; + } while (u >>= 4); + while (--s >= buf) + putchar(*s); + continue; +#endif case 'u': u = va_arg(ap, unsigned); s = buf; @@ -701,7 +732,7 @@ drvread(void *buf, unsigned lba, unsigned nblk) v86.edx = nblk << 8 | dsk.drive; v86int(); v86.ctl = V86_FLAGS; - if (V86_CY(v86.efl)) { + if (V86_CY(v86.efl) && !no_io_error) { printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba); return -1; } diff --git a/sys/boot/pc32/libi386/biosdisk.c b/sys/boot/pc32/libi386/biosdisk.c index 177be531a2..2ea4aab6fc 100644 --- a/sys/boot/pc32/libi386/biosdisk.c +++ b/sys/boot/pc32/libi386/biosdisk.c @@ -382,6 +382,7 @@ print_partition(u_int8_t fstype, unsigned long long offset, if ((fstype == FS_SWAP) || (fstype == FS_VINUM) || (fstype == FS_HAMMER) || + (fstype == FS_HAMMER2) || (fstype == FS_BSDFFS) || (fstype == FS_ZFS) || (fstype == FS_JFS2) || @@ -398,6 +399,7 @@ print_partition(u_int8_t fstype, unsigned long long offset, (fstype == FS_SWAP) ? "swap" : (fstype == FS_VINUM) ? "vinum" : (fstype == FS_HAMMER) ? "HAMMER" : + (fstype == FS_HAMMER2) ? "HAMMER2" : (fstype == FS_JFS2) ? "JFS2" : (fstype == FS_ZFS) ? "ZFS" : (fstype == FS_BSDFFS) ? "FFS" : @@ -414,6 +416,7 @@ print_partition(u_int8_t fstype, unsigned long long offset, (fstype == FS_SWAP) ? "swap" : (fstype == FS_VINUM) ? "vinum" : (fstype == FS_HAMMER) ? "HAMMER" : + (fstype == FS_HAMMER2) ? "HAMMER2" : (fstype == FS_JFS2) ? "JFS2" : (fstype == FS_ZFS) ? "ZFS" : (fstype == FS_BSDFFS) ? "FFS" : diff --git a/sys/boot/pc32/loader/dloader.rc b/sys/boot/pc32/loader/dloader.rc index 3c2fd40413..2a0d155010 100644 --- a/sys/boot/pc32/loader/dloader.rc +++ b/sys/boot/pc32/loader/dloader.rc @@ -3,7 +3,7 @@ # common/do_dloader.c has put us in "/boot" if it exists, # else we are in "/". It depends on whether the system # is configured with a single boot+root or a separate boot -# with a HAMMER root. +# with a HAMMER or HAMMER2 root. include defaults/loader.conf include defaults/dloader.menu diff --git a/sys/vfs/hammer2/hammer2_inode.c b/sys/vfs/hammer2/hammer2_inode.c index 49fe8165ef..855bca0ecc 100644 --- a/sys/vfs/hammer2/hammer2_inode.c +++ b/sys/vfs/hammer2/hammer2_inode.c @@ -559,6 +559,7 @@ hammer2_inode_create(hammer2_trans_t *trans, hammer2_inode_t *dip, uuid_t dip_uid; uuid_t dip_gid; uint32_t dip_mode; + uint8_t dip_algo; lhc = hammer2_dirhash(name, name_len); *errorp = 0; @@ -576,6 +577,7 @@ retry: dip_uid = dipdata->uid; dip_gid = dipdata->gid; dip_mode = dipdata->mode; + dip_algo = dipdata->comp_algo; error = 0; while (error == 0) { @@ -643,7 +645,7 @@ retry: /* Inherit parent's inode compression mode. */ nip->comp_heuristic = 0; - nipdata->comp_algo = dipdata->comp_algo; + nipdata->comp_algo = dip_algo; nipdata->version = HAMMER2_INODE_VERSION_ONE; hammer2_update_time(&nipdata->ctime); nipdata->mtime = nipdata->ctime; diff --git a/sys/vfs/hammer2/hammer2_ioctl.c b/sys/vfs/hammer2/hammer2_ioctl.c index dbfe896e5c..9cdd3bf248 100644 --- a/sys/vfs/hammer2/hammer2_ioctl.c +++ b/sys/vfs/hammer2/hammer2_ioctl.c @@ -508,6 +508,13 @@ hammer2_ioctl_pfs_create(hammer2_inode_t *ip, void *data) nipdata->pfs_type = pfs->pfs_type; nipdata->pfs_clid = pfs->pfs_clid; nipdata->pfs_fsid = pfs->pfs_fsid; + + /* + * Do not allow compression on PFS's with the special name + * "boot", the boot loader can't decompress (yet). + */ + if (strcmp(pfs->name, "boot") == 0) + nipdata->comp_algo = HAMMER2_COMP_AUTOZERO; hammer2_inode_unlock_ex(nip, nchain); } hammer2_trans_done(&trans);