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.2 2008/10/29 22:14:25 swildner 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
:
383 hprintb(hammer_base_elm_t e
)
385 printf("%d/", e
->localization
);
386 if (e
->obj_id
>> 32 != 0)
388 (long)(e
->obj_id
>> 32),
389 (long)(e
->obj_id
& 0xffffffff));
391 printf("%lx", (long)e
->obj_id
);
392 printf("/%d/", e
->rec_type
);
393 if (e
->key
>> 32 != 0)
395 (long)(e
->key
>> 32),
396 (long)(e
->key
& 0xffffffff));
398 printf("%lx", (long)e
->key
);
400 printf("/%llx/%llx", e
->create_tid
, e
->delete_tid
);
403 #endif /* DEBUG > 1 */
406 static hammer_btree_leaf_elm_t
407 hfind(struct hfs
*hfs
, hammer_base_elm_t key
, hammer_base_elm_t end
)
410 printf("searching for ");
417 struct hammer_base_elm search
= *key
;
418 struct hammer_base_elm backtrack
;
419 hammer_off_t nodeoff
= hfs
->root
;
420 hammer_node_ondisk_t node
;
421 hammer_btree_elm_t e
= NULL
;
424 node
= hread(hfs
, nodeoff
);
429 for (int i
= 0; i
< node
->count
; i
++) {
431 hprintb(&node
->elms
[i
].base
);
436 n
= hammer_btree_search_node(&search
, node
);
438 for (; n
< node
->count
; n
++) {
440 r
= hammer_btree_cmp(&search
, &e
->base
);
446 // unless we stopped right on the left side, we need to back off a bit
448 e
= &node
->elms
[n
- 1];
456 if (node
->type
== HAMMER_BTREE_TYPE_INTERNAL
) {
457 nodeoff
= e
->internal
.subtree_offset
;
458 backtrack
= (e
+1)->base
;
462 r
= hammer_btree_cmp(key
, &e
->base
);
463 // If we're more off than the createtid, take the next elem
469 // Skip deleted elements
470 while (n
< node
->count
&& e
->base
.delete_tid
!= 0) {
475 // In the unfortunate event when there is no next
476 // element in this node, we repeat the search with
477 // a key beyond the right boundary
478 if (n
== node
->count
) {
483 printf("hit right boundary (%d), resetting search to ",
498 if (hammer_btree_cmp(end
, &e
->base
) < -1)
512 hreaddir(struct hfs
*hfs
, ino_t ino
, int64_t *off
, struct dirent
*de
)
514 struct hammer_base_elm key
, end
;
516 bzero(&key
, sizeof(key
));
518 key
.localization
= HAMMER_LOCALIZE_MISC
;
519 key
.rec_type
= HAMMER_RECTYPE_DIRENTRY
;
523 end
.key
= HAMMER_MAX_KEY
;
525 hammer_btree_leaf_elm_t e
;
527 e
= hfind(hfs
, &key
, &end
);
533 *off
= e
->base
.key
+ 1; // remember next pos
535 de
->d_namlen
= e
->data_len
- HAMMER_ENTRY_NAME_OFF
;
536 de
->d_type
= hammer_get_dtype(e
->base
.obj_type
);
537 hammer_data_ondisk_t ed
= hread(hfs
, e
->data_offset
);
540 de
->d_ino
= ed
->entry
.obj_id
;
541 bcopy(ed
->entry
.name
, de
->d_name
, de
->d_namlen
);
542 de
->d_name
[de
->d_namlen
] = 0;
549 hresolve(struct hfs
*hfs
, ino_t dirino
, const char *name
)
551 struct hammer_base_elm key
, end
;
552 size_t namel
= strlen(name
);
554 bzero(&key
, sizeof(key
));
556 key
.localization
= HAMMER_LOCALIZE_MISC
;
557 key
.key
= hammer_directory_namekey(name
, namel
);
558 key
.rec_type
= HAMMER_RECTYPE_DIRENTRY
;
560 end
.key
= HAMMER_MAX_KEY
;
562 hammer_btree_leaf_elm_t e
;
563 while ((e
= hfind(hfs
, &key
, &end
)) != NULL
) {
564 key
.key
= e
->base
.key
+ 1;
566 size_t elen
= e
->data_len
- HAMMER_ENTRY_NAME_OFF
;
567 hammer_data_ondisk_t ed
= hread(hfs
, e
->data_offset
);
572 for (int i
= 0; i
< elen
; i
++)
573 putchar(ed
->entry
.name
[i
]);
579 if (elen
== namel
&& memcmp(ed
->entry
.name
, name
, MIN(elen
, namel
)) == 0)
580 return (ed
->entry
.obj_id
);
592 hlookup(struct hfs
*hfs
, const char *path
)
599 char name
[MAXPATHLEN
+ 1];
602 for (char *n
= name
; *path
!= 0 && *path
!= '/'; path
++, n
++) {
608 // A single ? means "list"
609 if (name
[0] == '?' && name
[1] == 0)
613 ino
= hresolve(hfs
, ino
, name
);
614 } while (ino
!= (ino_t
)-1 && *path
!= 0);
622 hstat(struct hfs
*hfs
, ino_t ino
, struct stat
* st
)
624 struct hammer_base_elm key
;
626 bzero(&key
, sizeof(key
));
628 key
.localization
= HAMMER_LOCALIZE_INODE
;
629 key
.rec_type
= HAMMER_RECTYPE_INODE
;
631 hammer_btree_leaf_elm_t e
= hfind(hfs
, &key
, &key
);
639 hammer_data_ondisk_t ed
= hread(hfs
, e
->data_offset
);
643 st
->st_mode
= ed
->inode
.mode
| hammer_get_mode(ed
->inode
.obj_type
);
644 st
->st_uid
= hammer_to_unix_xid(&ed
->inode
.uid
);
645 st
->st_gid
= hammer_to_unix_xid(&ed
->inode
.gid
);
646 st
->st_size
= ed
->inode
.size
;
653 hreadf(struct hfs
*hfs
, ino_t ino
, int64_t off
, int64_t len
, char *buf
)
655 int64_t startoff
= off
;
656 struct hammer_base_elm key
, end
;
658 bzero(&key
, sizeof(key
));
660 key
.localization
= HAMMER_LOCALIZE_MISC
;
661 key
.rec_type
= HAMMER_RECTYPE_DATA
;
663 end
.key
= HAMMER_MAX_KEY
;
667 hammer_btree_leaf_elm_t e
= hfind(hfs
, &key
, &end
);
670 if (e
== NULL
|| off
> e
->base
.key
) {
677 int64_t doff
= e
->base
.key
- e
->data_len
;
679 // sparse file, beginning
681 dlen
= MIN(dlen
, len
);
684 int64_t boff
= off
- doff
;
685 hammer_off_t roff
= e
->data_offset
;
689 dlen
= MIN(dlen
, len
);
691 while (boff
>= HAMMER_BUFSIZE
) {
692 boff
-= HAMMER_BUFSIZE
;
693 roff
+= HAMMER_BUFSIZE
;
696 // cut to HAMMER_BUFSIZE
697 if ((roff
& ~HAMMER_BUFMASK
) != ((roff
+ dlen
- 1) & ~HAMMER_BUFMASK
))
698 dlen
= HAMMER_BUFSIZE
- ((boff
+ roff
) & HAMMER_BUFMASK
);
700 char *data
= hread(hfs
, roff
);
703 bcopy(data
+ boff
, buf
, dlen
);
711 return (off
- startoff
);
723 hammer_volume_ondisk_t volhead
= hread(&hfs
, HAMMER_ZONE_ENCODE(1, 0));
726 if (volhead
->vol_signature
!= HAMMER_FSBUF_VOLUME
)
728 hfs
.root
= volhead
->vol0_btree_root
;
729 hfs
.buf_beg
= volhead
->vol_buf_beg
;
735 lookup(const char *path
)
739 ino_t ino
= hlookup(&hfs
, path
);
747 fsread(ino_t ino
, void *buf
, size_t len
)
751 ssize_t rlen
= hreadf(&hfs
, ino
, fs_off
, len
, buf
);
760 hinit(struct hfs
*hfs
)
765 for (int i
= 0; i
< NUMCACHE
; i
++) {
766 hfs
->cache
[i
].data
= malloc(HAMMER_BUFSIZE
);
767 hfs
->cache
[i
].off
= -1; // invalid
768 hfs
->cache
[i
].use
= 0;
771 if (hfs
->cache
[i
].data
== NULL
)
772 printf("malloc failed\n");
777 hammer_volume_ondisk_t volhead
= hread(hfs
, HAMMER_ZONE_ENCODE(1, 0));
782 printf("signature: %svalid\n",
783 volhead
->vol_signature
!= HAMMER_FSBUF_VOLUME
?
786 printf("name: %s\n", volhead
->vol_name
);
789 if (volhead
->vol_signature
!= HAMMER_FSBUF_VOLUME
) {
790 for (int i
= 0; i
< NUMCACHE
; i
++)
791 free(hfs
->cache
[i
].data
);
796 hfs
->root
= volhead
->vol0_btree_root
;
797 hfs
->buf_beg
= volhead
->vol_buf_beg
;
803 hclose(struct hfs
*hfs
)
808 for (int i
= 0; i
< NUMCACHE
; i
++)
809 free(hfs
->cache
[i
].data
);
821 hammer_open(const char *path
, struct open_file
*f
)
823 struct hfile
*hf
= malloc(sizeof(*hf
));
824 bzero(hf
, sizeof(*hf
));
830 int rv
= hinit(&hf
->hfs
);
837 printf("hammer_open %s %p %ld\n", path
, f
);
840 hf
->ino
= hlookup(&hf
->hfs
, path
);
845 if (hstat(&hf
->hfs
, hf
->ino
, &st
) == -1)
847 hf
->fsize
= st
.st_size
;
850 printf(" %ld\n", (long)hf
->fsize
);
857 printf("hammer_open fail\n");
865 hammer_close(struct open_file
*f
)
867 struct hfile
*hf
= f
->f_fsdata
;
876 hammer_read(struct open_file
*f
, void *buf
, size_t len
, size_t *resid
)
878 struct hfile
*hf
= f
->f_fsdata
;
881 printf("hammer_read %p %ld %ld\n", f
, f
->f_offset
, len
);
884 if (f
->f_offset
>= hf
->fsize
)
888 if (f
->f_offset
+ len
> hf
->fsize
)
889 maxlen
= hf
->fsize
- f
->f_offset
;
891 ssize_t rlen
= hreadf(&hf
->hfs
, hf
->ino
, f
->f_offset
, maxlen
, buf
);
902 hammer_seek(struct open_file
*f
, off_t offset
, int whence
)
904 struct hfile
*hf
= f
->f_fsdata
;
908 f
->f_offset
= offset
;
911 f
->f_offset
+= offset
;
914 f
->f_offset
= hf
->fsize
- offset
;
919 return (f
->f_offset
);
923 hammer_stat(struct open_file
*f
, struct stat
*st
)
925 struct hfile
*hf
= f
->f_fsdata
;
927 return (hstat(&hf
->hfs
, hf
->ino
, st
));
931 hammer_readdir(struct open_file
*f
, struct dirent
*d
)
933 struct hfile
*hf
= f
->f_fsdata
;
935 int64_t off
= f
->f_offset
;
936 int rv
= hreaddir(&hf
->hfs
, hf
->ino
, &off
, d
);
942 struct fs_ops hammer_fsops
= {
956 main(int argc
, char **argv
)
959 fprintf(stderr
, "usage: hammerread <dev>\n");
964 hfs
.fd
= open(argv
[1], O_RDONLY
);
966 err(1, "unable to open %s", argv
[1]);
968 if (hinit(&hfs
) == -1)
969 err(1, "invalid hammerfs");
971 for (int i
= 2; i
< argc
; i
++) {
972 ino_t ino
= hlookup(&hfs
, argv
[i
]);
973 if (ino
== (ino_t
)-1) {
974 warn("hlookup %s", argv
[i
]);
979 if (hstat(&hfs
, ino
, &st
)) {
980 warn("hstat %s", argv
[i
]);
984 printf("%s %d/%d %o %lld\n",
986 st
.st_uid
, st
.st_gid
,
987 st
.st_mode
, st
.st_size
);
989 if (S_ISDIR(st
.st_mode
)) {
992 while (hreaddir(&hfs
, ino
, &off
, &de
) == 0) {
993 printf("%s %d %llx\n",
994 de
.d_name
, de
.d_type
, de
.d_ino
);
996 } else if (S_ISREG(st
.st_mode
)) {
997 char *buf
= malloc(100000);
999 while (off
< st
.st_size
) {
1000 int64_t len
= MIN(100000, st
.st_size
- off
);
1001 int64_t rl
= hreadf(&hfs
, ino
, off
, len
, buf
);
1002 fwrite(buf
, rl
, 1, stdout
);