2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Simon Schubert <corecode@fs.ei.tum.de>
6 * and Matthew Dillon <dillon@backplane.com>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific, prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * $DragonFly: src/lib/libstand/hammerread.c,v 1.1 2008/09/13 11:38:58 corecode Exp $
39 * This file is being used by boot2 and libstand (loader).
40 * Compile with -DTESTING to obtain a binary.
44 #if !defined(BOOT2) && !defined(TESTING)
48 #include <sys/param.h>
54 #include <sys/fcntl.h>
69 #include <vfs/hammer/hammer_disk.h>
93 struct blockentry cache[NUMCACHE];
97 hread(struct hfs *hfs, hammer_off_t off)
99 hammer_off_t boff = off & ~HAMMER_BUFMASK;
101 boff &= HAMMER_OFF_LONG_MASK;
103 if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX)
104 boff += hfs->buf_beg;
106 struct blockentry *be = NULL;
107 for (int i = 0; i < NUMCACHE; i++) {
108 if (be == NULL || be->use > hfs->cache[i].use)
110 if (hfs->cache[i].off == boff) {
115 if (be->off != boff) {
116 // Didn't find any match
119 ssize_t res = pread(hfs->fd, be->data, HAMMER_BUFSIZE,
120 boff & HAMMER_OFF_SHORT_MASK);
121 if (res != HAMMER_BUFSIZE)
122 err(1, "short read on off %llx", boff);
125 int rv = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
126 boff >> DEV_BSHIFT, HAMMER_BUFSIZE,
128 if (rv || rlen != HAMMER_BUFSIZE)
133 be->use = ++hfs->lru;
134 return &be->data[off & HAMMER_BUFMASK];
140 char secbuf[DEV_BSIZE];
141 char buf[HAMMER_BUFSIZE];
144 static struct dmadat *dmadat;
152 hread(struct hfs *hfs, hammer_off_t off)
154 char *buf = dmadat->buf;
156 hammer_off_t boff = off & ~HAMMER_BUFMASK;
157 boff &= HAMMER_OFF_LONG_MASK;
158 if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX)
159 boff += hfs->buf_beg;
160 boff &= HAMMER_OFF_SHORT_MASK;
162 if (dskread(buf, boff, HAMMER_BUFSIZE >> DEV_BSHIFT))
164 return (&buf[off & HAMMER_BUFMASK]);
168 bzero(void *buf, size_t size)
170 for (size_t i = 0; i < size; i++)
171 ((char *)buf)[i] = 0;
175 bcopy(void *src, void *dst, size_t size)
177 memcpy(dst, src, size);
181 strlen(const char *s)
190 memcmp(const void *a, const void *b, size_t len)
192 for (size_t p = 0; p < len; p++) {
193 int r = ((const char *)a)[p] - ((const char *)b)[p];
204 * (from hammer_btree.c)
206 * Compare two B-Tree elements, return -N, 0, or +N (e.g. similar to strcmp).
208 * Note that for this particular function a return value of -1, 0, or +1
209 * can denote a match if create_tid is otherwise discounted. A create_tid
210 * of zero is considered to be 'infinity' in comparisons.
212 * See also hammer_rec_rb_compare() and hammer_rec_cmp() in hammer_object.c.
215 hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2)
217 if (key1->localization < key2->localization)
219 if (key1->localization > key2->localization)
222 if (key1->obj_id < key2->obj_id)
224 if (key1->obj_id > key2->obj_id)
227 if (key1->rec_type < key2->rec_type)
229 if (key1->rec_type > key2->rec_type)
232 if (key1->key < key2->key)
234 if (key1->key > key2->key)
238 * A create_tid of zero indicates a record which is undeletable
239 * and must be considered to have a value of positive infinity.
241 if (key1->create_tid == 0) {
242 if (key2->create_tid == 0)
246 if (key2->create_tid == 0)
248 if (key1->create_tid < key2->create_tid)
250 if (key1->create_tid > key2->create_tid)
256 * Heuristical search for the first element whos comparison is <= 1. May
257 * return an index whos compare result is > 1 but may only return an index
258 * whos compare result is <= 1 if it is the first element with that result.
261 hammer_btree_search_node(hammer_base_elm_t elm, hammer_node_ondisk_t node)
269 * Don't bother if the node does not have very many elements
275 r = hammer_btree_cmp(elm, &node->elms[i].leaf.base);
287 * (from hammer_subs.c)
289 * Return a namekey hash. The 64 bit namekey hash consists of a 32 bit
290 * crc in the MSB and 0 in the LSB. The caller will use the low bits to
291 * generate a unique key and will scan all entries with the same upper
292 * 32 bits when issuing a lookup.
294 * We strip bit 63 in order to provide a positive key, this way a seek
295 * offset of 0 will represent the base of the directory.
297 * This function can never return 0. We use the MSB-0 space to synthesize
298 * artificial directory entries such as "." and "..".
301 hammer_directory_namekey(const void *name, int len)
305 key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32;
307 key |= 0x100000000LL;
312 hammer_directory_namekey(const void *name __unused, int len __unused)
324 hammer_to_unix_xid(uuid_t *uuid)
326 return(*(u_int32_t *)&uuid->node[2]);
330 hammer_get_dtype(u_int8_t obj_type)
333 case HAMMER_OBJTYPE_DIRECTORY:
335 case HAMMER_OBJTYPE_REGFILE:
337 case HAMMER_OBJTYPE_DBFILE:
339 case HAMMER_OBJTYPE_FIFO:
341 case HAMMER_OBJTYPE_SOCKET:
343 case HAMMER_OBJTYPE_CDEV:
345 case HAMMER_OBJTYPE_BDEV:
347 case HAMMER_OBJTYPE_SOFTLINK:
356 hammer_get_mode(u_int8_t obj_type)
359 case HAMMER_OBJTYPE_DIRECTORY:
361 case HAMMER_OBJTYPE_REGFILE:
363 case HAMMER_OBJTYPE_DBFILE:
365 case HAMMER_OBJTYPE_FIFO:
367 case HAMMER_OBJTYPE_SOCKET:
369 case HAMMER_OBJTYPE_CDEV:
371 case HAMMER_OBJTYPE_BDEV:
373 case HAMMER_OBJTYPE_SOFTLINK:
382 hprintb(hammer_base_elm_t e)
384 printf("%d/", e->localization);
385 if (e->obj_id >> 32 != 0)
387 (long)(e->obj_id >> 32),
388 (long)(e->obj_id & 0xffffffff));
390 printf("%lx", (long)e->obj_id);
391 printf("/%d/", e->rec_type);
392 if (e->key >> 32 != 0)
394 (long)(e->key >> 32),
395 (long)(e->key & 0xffffffff));
397 printf("%lx", (long)e->key);
399 printf("/%llx/%llx", e->create_tid, e->delete_tid);
404 static hammer_btree_leaf_elm_t
405 hfind(struct hfs *hfs, hammer_base_elm_t key, hammer_base_elm_t end)
408 printf("searching for ");
415 struct hammer_base_elm search = *key;
416 struct hammer_base_elm backtrack;
417 hammer_off_t nodeoff = hfs->root;
418 hammer_node_ondisk_t node;
419 hammer_btree_elm_t e;
422 node = hread(hfs, nodeoff);
427 for (int i = 0; i < node->count; i++) {
429 hprintb(&node->elms[i].base);
434 n = hammer_btree_search_node(&search, node);
436 for (; n < node->count; n++) {
438 r = hammer_btree_cmp(&search, &e->base);
444 // unless we stopped right on the left side, we need to back off a bit
446 e = &node->elms[n - 1];
454 if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
455 nodeoff = e->internal.subtree_offset;
456 backtrack = (e+1)->base;
460 r = hammer_btree_cmp(key, &e->base);
461 // If we're more off than the createtid, take the next elem
467 // Skip deleted elements
468 while (n < node->count && e->base.delete_tid != 0) {
473 // In the unfortunate event when there is no next
474 // element in this node, we repeat the search with
475 // a key beyond the right boundary
476 if (n == node->count) {
481 printf("hit right boundary (%d), resetting search to ",
496 if (hammer_btree_cmp(end, &e->base) < -1)
510 hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de)
512 struct hammer_base_elm key, end;
514 bzero(&key, sizeof(key));
516 key.localization = HAMMER_LOCALIZE_MISC;
517 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
521 end.key = HAMMER_MAX_KEY;
523 hammer_btree_leaf_elm_t e;
525 e = hfind(hfs, &key, &end);
531 *off = e->base.key + 1; // remember next pos
533 de->d_namlen = e->data_len - HAMMER_ENTRY_NAME_OFF;
534 de->d_type = hammer_get_dtype(e->base.obj_type);
535 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
538 de->d_ino = ed->entry.obj_id;
539 bcopy(ed->entry.name, de->d_name, de->d_namlen);
540 de->d_name[de->d_namlen] = 0;
547 hresolve(struct hfs *hfs, ino_t dirino, const char *name)
549 struct hammer_base_elm key, end;
550 size_t namel = strlen(name);
552 bzero(&key, sizeof(key));
554 key.localization = HAMMER_LOCALIZE_MISC;
555 key.key = hammer_directory_namekey(name, namel);
556 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
558 end.key = HAMMER_MAX_KEY;
560 hammer_btree_leaf_elm_t e;
561 while ((e = hfind(hfs, &key, &end)) != NULL) {
562 key.key = e->base.key + 1;
564 size_t elen = e->data_len - HAMMER_ENTRY_NAME_OFF;
565 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
570 for (int i = 0; i < elen; i++)
571 putchar(ed->entry.name[i]);
577 if (elen == namel && memcmp(ed->entry.name, name, MIN(elen, namel)) == 0)
578 return (ed->entry.obj_id);
590 hlookup(struct hfs *hfs, const char *path)
597 char name[MAXPATHLEN + 1];
600 for (char *n = name; *path != 0 && *path != '/'; path++, n++) {
606 // A single ? means "list"
607 if (name[0] == '?' && name[1] == 0)
611 ino = hresolve(hfs, ino, name);
612 } while (ino != (ino_t)-1 && *path != 0);
620 hstat(struct hfs *hfs, ino_t ino, struct stat* st)
622 struct hammer_base_elm key;
624 bzero(&key, sizeof(key));
626 key.localization = HAMMER_LOCALIZE_INODE;
627 key.rec_type = HAMMER_RECTYPE_INODE;
629 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &key);
637 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
641 st->st_mode = ed->inode.mode | hammer_get_mode(ed->inode.obj_type);
642 st->st_uid = hammer_to_unix_xid(&ed->inode.uid);
643 st->st_gid = hammer_to_unix_xid(&ed->inode.gid);
644 st->st_size = ed->inode.size;
651 hreadf(struct hfs *hfs, ino_t ino, int64_t off, int64_t len, char *buf)
653 int64_t startoff = off;
654 struct hammer_base_elm key, end;
656 bzero(&key, sizeof(key));
658 key.localization = HAMMER_LOCALIZE_MISC;
659 key.rec_type = HAMMER_RECTYPE_DATA;
661 end.key = HAMMER_MAX_KEY;
665 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &end);
668 if (e == NULL || off > e->base.key) {
675 int64_t doff = e->base.key - e->data_len;
677 // sparse file, beginning
679 dlen = MIN(dlen, len);
682 int64_t boff = off - doff;
683 hammer_off_t roff = e->data_offset;
687 dlen = MIN(dlen, len);
689 while (boff >= HAMMER_BUFSIZE) {
690 boff -= HAMMER_BUFSIZE;
691 roff += HAMMER_BUFSIZE;
694 // cut to HAMMER_BUFSIZE
695 if ((roff & ~HAMMER_BUFMASK) != ((roff + dlen - 1) & ~HAMMER_BUFMASK))
696 dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK);
698 char *data = hread(hfs, roff);
701 bcopy(data + boff, buf, dlen);
709 return (off - startoff);
721 hammer_volume_ondisk_t volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0));
724 if (volhead->vol_signature != HAMMER_FSBUF_VOLUME)
726 hfs.root = volhead->vol0_btree_root;
727 hfs.buf_beg = volhead->vol_buf_beg;
733 lookup(const char *path)
737 ino_t ino = hlookup(&hfs, path);
745 fsread(ino_t ino, void *buf, size_t len)
749 ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf);
758 hinit(struct hfs *hfs)
763 for (int i = 0; i < NUMCACHE; i++) {
764 hfs->cache[i].data = malloc(HAMMER_BUFSIZE);
765 hfs->cache[i].off = -1; // invalid
766 hfs->cache[i].use = 0;
769 if (hfs->cache[i].data == NULL)
770 printf("malloc failed\n");
775 hammer_volume_ondisk_t volhead = hread(hfs, HAMMER_ZONE_ENCODE(1, 0));
780 printf("signature: %svalid\n",
781 volhead->vol_signature != HAMMER_FSBUF_VOLUME ?
784 printf("name: %s\n", volhead->vol_name);
787 if (volhead->vol_signature != HAMMER_FSBUF_VOLUME) {
788 for (int i = 0; i < NUMCACHE; i++)
789 free(hfs->cache[i].data);
794 hfs->root = volhead->vol0_btree_root;
795 hfs->buf_beg = volhead->vol_buf_beg;
801 hclose(struct hfs *hfs)
806 for (int i = 0; i < NUMCACHE; i++)
807 free(hfs->cache[i].data);
819 hammer_open(const char *path, struct open_file *f)
821 struct hfile *hf = malloc(sizeof(*hf));
822 bzero(hf, sizeof(*hf));
828 int rv = hinit(&hf->hfs);
835 printf("hammer_open %s %p %ld\n", path, f);
838 hf->ino = hlookup(&hf->hfs, path);
843 if (hstat(&hf->hfs, hf->ino, &st) == -1)
845 hf->fsize = st.st_size;
848 printf(" %ld\n", (long)hf->fsize);
855 printf("hammer_open fail\n");
863 hammer_close(struct open_file *f)
865 struct hfile *hf = f->f_fsdata;
874 hammer_read(struct open_file *f, void *buf, size_t len, size_t *resid)
876 struct hfile *hf = f->f_fsdata;
879 printf("hammer_read %p %ld %ld\n", f, f->f_offset, len);
882 if (f->f_offset >= hf->fsize)
886 if (f->f_offset + len > hf->fsize)
887 maxlen = hf->fsize - f->f_offset;
889 ssize_t rlen = hreadf(&hf->hfs, hf->ino, f->f_offset, maxlen, buf);
900 hammer_seek(struct open_file *f, off_t offset, int whence)
902 struct hfile *hf = f->f_fsdata;
906 f->f_offset = offset;
909 f->f_offset += offset;
912 f->f_offset = hf->fsize - offset;
917 return (f->f_offset);
921 hammer_stat(struct open_file *f, struct stat *st)
923 struct hfile *hf = f->f_fsdata;
925 return (hstat(&hf->hfs, hf->ino, st));
929 hammer_readdir(struct open_file *f, struct dirent *d)
931 struct hfile *hf = f->f_fsdata;
933 int64_t off = f->f_offset;
934 int rv = hreaddir(&hf->hfs, hf->ino, &off, d);
940 struct fs_ops hammer_fsops = {
954 main(int argc, char **argv)
957 fprintf(stderr, "usage: hammerread <dev>\n");
962 hfs.fd = open(argv[1], O_RDONLY);
964 err(1, "unable to open %s", argv[1]);
966 if (hinit(&hfs) == -1)
967 err(1, "invalid hammerfs");
969 for (int i = 2; i < argc; i++) {
970 ino_t ino = hlookup(&hfs, argv[i]);
971 if (ino == (ino_t)-1) {
972 warn("hlookup %s", argv[i]);
977 if (hstat(&hfs, ino, &st)) {
978 warn("hstat %s", argv[i]);
982 printf("%s %d/%d %o %lld\n",
984 st.st_uid, st.st_gid,
985 st.st_mode, st.st_size);
987 if (S_ISDIR(st.st_mode)) {
990 while (hreaddir(&hfs, ino, &off, &de) == 0) {
991 printf("%s %d %llx\n",
992 de.d_name, de.d_type, de.d_ino);
994 } else if (S_ISREG(st.st_mode)) {
995 char *buf = malloc(100000);
997 while (off < st.st_size) {
998 int64_t len = MIN(100000, st.st_size - off);
999 int64_t rl = hreadf(&hfs, ino, off, len, buf);
1000 fwrite(buf, rl, 1, stdout);