2 * Copyright (c) 2011-2013 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@dragonflybsd.org>
6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
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
36 #if !defined(BOOT2) && !defined(TESTING)
45 #include <sys/types.h>
63 #include <vfs/hammer2/hammer2_disk.h>
65 uint32_t iscsi_crc32(const void *buf
, size_t size
);
66 uint32_t iscsi_crc32_ext(const void *buf
, size_t size
, uint32_t ocrc
);
68 static hammer2_media_data_t media
;
69 static hammer2_blockref_t saved_base
;
71 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size)
74 hammer2_blockref_t sroot
;
75 hammer2_blockset_t sroot_blockset
;
78 #elif defined(LIBSTAND)
81 /* BOOT2 doesn't use a descriptor */
83 #error "hammer2: unknown library API"
87 struct hammer2_inode
{
88 struct hammer2_inode_data ino
; /* raw inode data */
89 off_t doff
; /* disk inode offset */
95 bzero(void *buf
, size_t size
)
97 for (size_t i
= 0; i
< size
; i
++)
102 bcopy(void *src
, void *dst
, size_t size
)
104 memcpy(dst
, src
, size
);
109 strlen(const char *s
)
119 memcmp(const void *a
, const void *b
, size_t len
)
121 for (size_t p
= 0; p
< len
; p
++) {
122 int r
= ((const char *)a
)[p
] - ((const char *)b
)[p
];
134 blockoff(hammer2_blockref_t
*bref
)
136 return(bref
->data_off
& ~HAMMER2_OFF_MASK_RADIX
);
141 blocksize(hammer2_blockref_t
*bref
)
145 bytes
= (size_t)(bref
->data_off
& HAMMER2_OFF_MASK_RADIX
);
147 bytes
= (size_t)1 << bytes
;
153 hammer2_dirhash(const unsigned char *name
, size_t len
)
155 const unsigned char *aname
= name
;
167 for (i
= j
= 0; i
< len
; ++i
) {
168 if (aname
[i
] == '.' ||
173 crcx
+= hammer2_icrc32(aname
+ j
, i
- j
);
178 crcx
+= hammer2_icrc32(aname
+ j
, i
- j
);
181 * The directory hash utilizes the top 32 bits of the 64-bit key.
182 * Bit 63 must be set to 1.
185 key
|= (uint64_t)crcx
<< 32;
188 * l16 - crc of entire filename
190 * This crc reduces degenerate hash collision conditions
192 crcx
= hammer2_icrc32(aname
, len
);
193 crcx
= crcx
^ (crcx
<< 16);
194 key
|= crcx
& 0xFFFF0000U
;
197 * Set bit 15. This allows readdir to strip bit 63 so a positive
198 * 64-bit cookie/offset can always be returned, and still guarantee
199 * that the values 0x0000-0x7FFF are available for artificial entries.
212 h2read(struct hammer2_fs
*hfs
, void *buf
, size_t nbytes
, off_t off
)
214 #if defined(LIBSTAND)
220 rc
= pread(hfs
->fd
, &media
, nbytes
, off
);
221 if (rc
== (int)nbytes
)
225 #elif defined(LIBSTAND)
226 rc
= hfs
->f
->f_dev
->dv_strategy(hfs
->f
->f_devdata
, F_READ
,
227 off
>> DEV_BSHIFT
, nbytes
,
229 if (rc
|| rlen
!= nbytes
)
232 /* BIOS interface may barf on 64KB reads */
234 while (nbytes
> 16384) {
235 rc
= dskread(buf
, off
>> DEV_BSHIFT
, 16384 >> DEV_BSHIFT
);
237 buf
= (char *)buf
+ 16384;
241 rc
= dskread(buf
, off
>> DEV_BSHIFT
, nbytes
>> DEV_BSHIFT
);
245 #error "hammer2: unknown library API"
253 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
254 * its device file descriptor initialized.
258 * Lookup within the block specified by (*base), loading the block from disk
259 * if necessary. Locate the first key within the requested range and
260 * recursively run through indirect blocks as needed. The caller may loop
261 * by setting key_beg to *key_ret.
263 * Returns 0 if nothing could be found and the key range has been exhausted.
264 * returns -1 if a disk error occured. Otherwise returns the size of the
265 * data block and returns the data block in *pptr and bref in *bref_ret.
267 * NOTE! When reading file data, the returned bref's key will be the nearest
268 * data block we looked up. The file read procedure must handle any
269 * zero-fill or skip. However, we will truncate the return value to
273 h2lookup(struct hammer2_fs
*hfs
, hammer2_blockref_t
*base
,
274 hammer2_key_t key_beg
, hammer2_key_t key_end
,
275 hammer2_blockref_t
*bref_ret
, void **pptr
)
277 hammer2_blockref_t
*bref
;
278 hammer2_blockref_t best
;
279 hammer2_key_t scan_beg
;
280 hammer2_key_t scan_end
;
288 saved_base
.data_off
= (hammer2_off_t
)-1;
291 if (base
->data_off
== (hammer2_off_t
)-1) {
297 * Calculate the number of blockrefs to scan
300 case HAMMER2_BREF_TYPE_VOLUME
:
301 count
= HAMMER2_SET_COUNT
;
303 case HAMMER2_BREF_TYPE_INODE
:
304 count
= HAMMER2_SET_COUNT
;
306 case HAMMER2_BREF_TYPE_INDIRECT
:
307 count
= blocksize(base
) / sizeof(hammer2_blockref_t
);
315 * Find the best candidate (the lowest blockref within the specified
316 * range). The array can be fully set associative so make no ordering
320 best
.key
= HAMMER2_KEY_MAX
;
323 for (i
= 0; i
< count
; ++i
) {
325 * [re]load when returning from our recursion
327 if (base
->type
!= HAMMER2_BREF_TYPE_VOLUME
&&
328 base
->data_off
!= saved_base
.data_off
) {
329 if (blocksize(base
) && h2read(hfs
, &media
,
339 * Special case embedded file data
341 if (base
->type
== HAMMER2_BREF_TYPE_INODE
) {
342 if (media
.ipdata
.meta
.op_flags
&
343 HAMMER2_OPFLAG_DIRECTDATA
) {
344 *pptr
= media
.ipdata
.u
.data
;
345 bref_ret
->type
= HAMMER2_BREF_TYPE_DATA
;
347 return HAMMER2_EMBEDDED_BYTES
;
352 * Calculate the bref in our scan.
355 case HAMMER2_BREF_TYPE_VOLUME
:
356 bref
= &hfs
->sroot_blockset
.blockref
[i
];
358 case HAMMER2_BREF_TYPE_INODE
:
359 bref
= &media
.ipdata
.u
.blockset
.blockref
[i
];
361 case HAMMER2_BREF_TYPE_INDIRECT
:
362 bref
= &media
.npdata
[i
];
367 if (bref
->key
> best
.key
)
369 scan_beg
= bref
->key
;
370 scan_end
= scan_beg
+ ((hammer2_key_t
)1 << bref
->keybits
) - 1;
371 if (scan_end
>= key_beg
&& scan_beg
<= key_end
) {
377 * Figure out what to do with the results.
387 case HAMMER2_BREF_TYPE_INDIRECT
:
389 * Matched an indirect block. If the block turns out to
390 * contain nothing we continue the iteration, otherwise
391 * we return the data from the recursion.
393 * Be sure to handle the overflow case when recalculating
396 rc
= h2lookup(hfs
, &best
, key_beg
, key_end
, bref_ret
, pptr
);
399 ((hammer2_key_t
)1 << best
.keybits
);
400 if (key_beg
> best
.key
&& key_beg
<= key_end
)
404 case HAMMER2_BREF_TYPE_DIRENT
:
405 case HAMMER2_BREF_TYPE_INODE
:
406 case HAMMER2_BREF_TYPE_DATA
:
408 * Terminal match. Leaf elements might not be data-aligned.
410 dev_bsize
= blocksize(&best
);
412 if (dev_bsize
< HAMMER2_LBUFSIZE
)
413 dev_bsize
= HAMMER2_LBUFSIZE
;
414 dev_boff
= blockoff(&best
) -
415 (blockoff(&best
) & ~HAMMER2_LBUFMASK64
);
416 if (h2read(hfs
, &media
,
418 blockoff(&best
) - dev_boff
)) {
422 saved_base
.data_off
= (hammer2_off_t
)-1;
424 *pptr
= media
.buf
+ dev_boff
;
425 rc
= blocksize(&best
);
433 h2resolve(struct hammer2_fs
*hfs
, const char *path
,
434 hammer2_blockref_t
*bref
, hammer2_inode_data_t
**inop
)
436 hammer2_blockref_t bres
;
437 hammer2_inode_data_t
*ino
;
444 * Start point (superroot)
452 * Iterate path elements
457 if (*path
== 0) /* terminal */
461 * Calculate path element and look for it in the directory
463 for (len
= 0; path
[len
]; ++len
) {
464 if (path
[len
] == '/')
467 key
= hammer2_dirhash(path
, len
);
469 bytes
= h2lookup(hfs
, bref
,
471 &bres
, (void **)&data
);
477 case HAMMER2_BREF_TYPE_DIRENT
:
478 if (bres
.embed
.dirent
.namlen
!= len
)
480 if (bres
.embed
.dirent
.namlen
<=
481 sizeof(bres
.check
.buf
)) {
482 if (memcmp(path
, bres
.check
.buf
, len
))
485 if (memcmp(path
, data
, len
))
490 * Found, resolve inode. This will set
491 * ino similarly to HAMMER2_BREF_TYPE_INODE
492 * and adjust bres, which path continuation
496 bytes
= h2lookup(hfs
, bref
,
497 bres
.embed
.dirent
.inum
,
498 bres
.embed
.dirent
.inum
,
499 &bres
, (void **)&ino
);
503 break; /* NOT REACHED */
504 case HAMMER2_BREF_TYPE_INODE
:
506 if (ino
->meta
.name_len
!= len
)
508 if (memcmp(path
, ino
->filename
, len
) == 0) {
515 if ((bres
.key
& 0xFFFF) == 0xFFFF) {
526 if (bytes
< 0 || bres
.type
== 0) {
527 bref
->data_off
= (hammer2_off_t
)-1;
533 * Check path continuance, inode must be a directory or
537 if (*path
&& ino
->meta
.type
!= HAMMER2_OBJTYPE_DIRECTORY
) {
538 bref
->data_off
= (hammer2_off_t
)-1;
547 h2readfile(struct hammer2_fs
*hfs
, hammer2_blockref_t
*bref
,
548 off_t off
, off_t filesize
, void *buf
, size_t len
)
550 hammer2_blockref_t bres
;
561 if (off
+ len
> filesize
)
562 len
= filesize
- off
;
570 * Find closest bres >= requested offset.
572 bytes
= h2lookup(hfs
, bref
, off
, off
+ len
- 1,
573 &bres
, (void **)&data
);
582 * Load the data into the buffer. First handle a degenerate
592 * Returned record overlaps to the left of the requested
593 * position. It must overlap in this case or h2lookup()
594 * would have returned something else.
596 if (bres
.key
< off
) {
597 data
+= off
- bres
.key
;
598 bytes
-= off
- bres
.key
;
602 * Returned record overlaps to the right of the requested
603 * position, handle zero-fill. Again h2lookup() only returns
604 * this case if there is an actual overlap.
606 if (bres
.key
> off
) {
607 zfill
= (ssize_t
)(bres
.key
- off
);
612 buf
= (char *)buf
+ zfill
;
616 * Trim returned request before copying.
620 bcopy(data
, buf
, bytes
);
624 buf
= (char *)buf
+ bytes
;
631 h2init(struct hammer2_fs
*hfs
)
636 hammer2_tid_t best_tid
= 0;
644 * Find the best volume header.
646 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
647 * given a disk offset beyond the EOM. XXX We need to probe the
648 * size of the media and limit our accesses, until then we have
649 * to give up if the first volume header does not have a hammer2
652 * XXX Probably still going to be problems w/ HAMMER2 volumes on
653 * media which is too small w/certain BIOSes.
656 for (i
= 0; i
< HAMMER2_NUM_VOLHDRS
; ++i
) {
657 off
= i
* HAMMER2_ZONE_BYTES64
;
660 if (h2read(hfs
, &media
, sizeof(media
.voldata
), off
))
662 if (media
.voldata
.magic
!= HAMMER2_VOLUME_ID_HBO
)
664 if (best
< 0 || best_tid
< media
.voldata
.mirror_tid
) {
666 best_tid
= media
.voldata
.mirror_tid
;
674 * Reload the best volume header and set up the blockref.
675 * We messed with media, clear the cache before continuing.
677 off
= best
* HAMMER2_ZONE_BYTES64
;
678 if (h2read(hfs
, &media
, sizeof(media
.voldata
), off
))
680 hfs
->sroot
.type
= HAMMER2_BREF_TYPE_VOLUME
;
681 hfs
->sroot
.data_off
= off
;
682 hfs
->sroot_blockset
= media
.voldata
.sroot_blockset
;
683 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
686 * Lookup sroot/BOOT and clear the cache again.
688 r
= h2lookup(hfs
, &hfs
->sroot
,
689 HAMMER2_SROOT_KEY
, HAMMER2_SROOT_KEY
,
693 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
694 r
= h2lookup(hfs
, &hfs
->sroot
,
695 HAMMER2_BOOT_KEY
, HAMMER2_BOOT_KEY
,
698 printf("hammer2: 'BOOT' PFS not found\n");
701 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
706 /************************************************************************
708 ************************************************************************
713 static struct hammer2_fs hfs
;
716 boot2_hammer2_init(void)
724 boot2_hammer2_lookup(const char *path
)
726 hammer2_blockref_t bref
;
728 h2resolve(&hfs
, path
, &bref
, NULL
);
729 return ((boot2_ino_t
)bref
.data_off
);
733 boot2_hammer2_read(boot2_ino_t ino
, void *buf
, size_t len
)
735 hammer2_blockref_t bref
;
738 bzero(&bref
, sizeof(bref
));
739 bref
.type
= HAMMER2_BREF_TYPE_INODE
;
742 total
= h2readfile(&hfs
, &bref
, fs_off
, 0x7FFFFFFF, buf
, len
);
748 const struct boot2_fsapi boot2_hammer2_api
= {
749 .fsinit
= boot2_hammer2_init
,
750 .fslookup
= boot2_hammer2_lookup
,
751 .fsread
= boot2_hammer2_read
756 /************************************************************************
758 ************************************************************************
764 struct hammer2_fs hfs
;
765 hammer2_blockref_t bref
;
773 hammer2_get_dtype(uint8_t type
)
776 case HAMMER2_OBJTYPE_DIRECTORY
:
778 case HAMMER2_OBJTYPE_REGFILE
:
780 case HAMMER2_OBJTYPE_FIFO
:
782 case HAMMER2_OBJTYPE_CDEV
:
784 case HAMMER2_OBJTYPE_BDEV
:
786 case HAMMER2_OBJTYPE_SOFTLINK
:
788 case HAMMER2_OBJTYPE_SOCKET
:
797 hammer2_get_mode(uint8_t type
)
800 case HAMMER2_OBJTYPE_DIRECTORY
:
802 case HAMMER2_OBJTYPE_REGFILE
:
804 case HAMMER2_OBJTYPE_FIFO
:
806 case HAMMER2_OBJTYPE_CDEV
:
808 case HAMMER2_OBJTYPE_BDEV
:
810 case HAMMER2_OBJTYPE_SOFTLINK
:
812 case HAMMER2_OBJTYPE_SOCKET
:
820 hammer2_open(const char *path
, struct open_file
*f
)
822 struct hfile
*hf
= malloc(sizeof(*hf
));
823 hammer2_inode_data_t
*ipdata
;
825 bzero(hf
, sizeof(*hf
));
830 if (h2init(&hf
->hfs
)) {
836 h2resolve(&hf
->hfs
, path
, &hf
->bref
, &ipdata
);
837 if (hf
->bref
.data_off
== (hammer2_off_t
)-1 ||
838 (hf
->bref
.type
!= HAMMER2_BREF_TYPE_INODE
&&
839 hf
->bref
.type
!= HAMMER2_BREF_TYPE_VOLUME
)) {
846 hf
->fsize
= ipdata
->meta
.size
;
847 hf
->type
= ipdata
->meta
.type
;
848 hf
->mode
= ipdata
->meta
.mode
|
849 hammer2_get_mode(ipdata
->meta
.type
);
852 hf
->type
= HAMMER2_OBJTYPE_DIRECTORY
;
853 hf
->mode
= 0755 | S_IFDIR
;
859 hammer2_close(struct open_file
*f
)
861 struct hfile
*hf
= f
->f_fsdata
;
870 hammer2_read(struct open_file
*f
, void *buf
, size_t len
, size_t *resid
)
872 struct hfile
*hf
= f
->f_fsdata
;
876 total
= h2readfile(&hf
->hfs
, &hf
->bref
,
877 f
->f_offset
, hf
->fsize
, buf
, len
);
882 f
->f_offset
+= total
;
885 *resid
= len
- total
;
890 hammer2_seek(struct open_file
*f
, off_t offset
, int whence
)
892 struct hfile
*hf
= f
->f_fsdata
;
896 f
->f_offset
= offset
;
899 f
->f_offset
+= offset
;
902 f
->f_offset
= hf
->fsize
- offset
;
907 return (f
->f_offset
);
911 hammer2_stat(struct open_file
*f
, struct stat
*st
)
913 struct hfile
*hf
= f
->f_fsdata
;
915 st
->st_mode
= hf
->mode
;
918 st
->st_size
= hf
->fsize
;
924 hammer2_readdir(struct open_file
*f
, struct dirent
*den
)
926 struct hfile
*hf
= f
->f_fsdata
;
927 hammer2_blockref_t bres
;
928 hammer2_inode_data_t
*ipdata
;
933 bytes
= h2lookup(&hf
->hfs
, &hf
->bref
,
934 f
->f_offset
| HAMMER2_DIRHASH_VISIBLE
,
936 &bres
, (void **)&data
);
940 case HAMMER2_BREF_TYPE_INODE
:
942 den
->d_namlen
= ipdata
->meta
.name_len
;
943 den
->d_type
= hammer2_get_dtype(ipdata
->meta
.type
);
944 den
->d_ino
= ipdata
->meta
.inum
;
945 bcopy(ipdata
->filename
, den
->d_name
, den
->d_namlen
);
946 den
->d_name
[den
->d_namlen
] = 0;
948 case HAMMER2_BREF_TYPE_DIRENT
:
949 den
->d_namlen
= bres
.embed
.dirent
.namlen
;
950 den
->d_type
= hammer2_get_dtype(bres
.embed
.dirent
.type
);
951 den
->d_ino
= bres
.embed
.dirent
.inum
;
952 if (den
->d_namlen
<= sizeof(bres
.check
.buf
)) {
953 bcopy(bres
.check
.buf
,
957 bcopy(data
, den
->d_name
, den
->d_namlen
);
959 den
->d_name
[den
->d_namlen
] = 0;
964 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE
);
965 den
->d_name
[0] = '?';
970 f
->f_offset
= bres
.key
+ 1;
977 struct fs_ops hammer_fsops
= {