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)
49 #include <sys/types.h>
63 #include <machine/param.h> /* for DEV_BSHIFT */
64 #include <vfs/hammer2/hammer2_disk.h>
66 uint32_t iscsi_crc32(const void *buf
, size_t size
);
67 uint32_t iscsi_crc32_ext(const void *buf
, size_t size
, uint32_t ocrc
);
69 static hammer2_media_data_t media
;
70 static hammer2_blockref_t saved_base
;
72 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size)
75 hammer2_blockref_t sroot
;
76 hammer2_blockset_t sroot_blockset
;
79 #elif defined(LIBSTAND)
82 /* BOOT2 doesn't use a descriptor */
84 #error "hammer2: unknown library API"
88 struct hammer2_inode
{
89 struct hammer2_inode_data ino
; /* raw inode data */
90 off_t doff
; /* disk inode offset */
96 bzero(void *buf
, size_t size
)
98 for (size_t i
= 0; i
< size
; i
++)
103 bcopy(void *src
, void *dst
, size_t size
)
105 memcpy(dst
, src
, size
);
110 strlen(const char *s
)
120 memcmp(const void *a
, const void *b
, size_t len
)
122 for (size_t p
= 0; p
< len
; p
++) {
123 int r
= ((const char *)a
)[p
] - ((const char *)b
)[p
];
135 blockoff(hammer2_blockref_t
*bref
)
137 return(bref
->data_off
& ~HAMMER2_OFF_MASK_RADIX
);
142 blocksize(hammer2_blockref_t
*bref
)
146 bytes
= (size_t)(bref
->data_off
& HAMMER2_OFF_MASK_RADIX
);
148 bytes
= (size_t)1 << bytes
;
154 hammer2_dirhash(const unsigned char *name
, size_t len
)
156 const unsigned char *aname
= name
;
168 for (i
= j
= 0; i
< len
; ++i
) {
169 if (aname
[i
] == '.' ||
174 crcx
+= hammer2_icrc32(aname
+ j
, i
- j
);
179 crcx
+= hammer2_icrc32(aname
+ j
, i
- j
);
182 * The directory hash utilizes the top 32 bits of the 64-bit key.
183 * Bit 63 must be set to 1.
186 key
|= (uint64_t)crcx
<< 32;
189 * l16 - crc of entire filename
191 * This crc reduces degenerate hash collision conditions
193 crcx
= hammer2_icrc32(aname
, len
);
194 crcx
= crcx
^ (crcx
<< 16);
195 key
|= crcx
& 0xFFFF0000U
;
198 * Set bit 15. This allows readdir to strip bit 63 so a positive
199 * 64-bit cookie/offset can always be returned, and still guarantee
200 * that the values 0x0000-0x7FFF are available for artificial entries.
213 h2read(struct hammer2_fs
*hfs
, void *buf
, size_t nbytes
, off_t off
)
215 #if defined(LIBSTAND)
221 rc
= pread(hfs
->fd
, &media
, nbytes
, off
);
222 if (rc
== (int)nbytes
)
226 #elif defined(LIBSTAND)
227 rc
= hfs
->f
->f_dev
->dv_strategy(hfs
->f
->f_devdata
, F_READ
,
228 off
>> DEV_BSHIFT
, nbytes
,
230 if (rc
|| rlen
!= nbytes
)
233 /* BIOS interface may barf on 64KB reads */
235 while (nbytes
> 16384) {
236 rc
= dskread(buf
, off
>> DEV_BSHIFT
, 16384 >> DEV_BSHIFT
);
238 buf
= (char *)buf
+ 16384;
242 rc
= dskread(buf
, off
>> DEV_BSHIFT
, nbytes
>> DEV_BSHIFT
);
246 #error "hammer2: unknown library API"
254 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
255 * its device file descriptor initialized.
259 * Lookup within the block specified by (*base), loading the block from disk
260 * if necessary. Locate the first key within the requested range and
261 * recursively run through indirect blocks as needed. The caller may loop
262 * by setting key_beg to *key_ret.
264 * Returns 0 if nothing could be found and the key range has been exhausted.
265 * returns -1 if a disk error occured. Otherwise returns the size of the
266 * data block and returns the data block in *pptr and bref in *bref_ret.
268 * NOTE! When reading file data, the returned bref's key will be the nearest
269 * data block we looked up. The file read procedure must handle any
270 * zero-fill or skip. However, we will truncate the return value to
274 h2lookup(struct hammer2_fs
*hfs
, hammer2_blockref_t
*base
,
275 hammer2_key_t key_beg
, hammer2_key_t key_end
,
276 hammer2_blockref_t
*bref_ret
, void **pptr
)
278 hammer2_blockref_t
*bref
;
279 hammer2_blockref_t best
;
280 hammer2_key_t scan_beg
;
281 hammer2_key_t scan_end
;
289 saved_base
.data_off
= (hammer2_off_t
)-1;
292 if (base
->data_off
== (hammer2_off_t
)-1) {
298 * Calculate the number of blockrefs to scan
301 case HAMMER2_BREF_TYPE_VOLUME
:
302 count
= HAMMER2_SET_COUNT
;
304 case HAMMER2_BREF_TYPE_INODE
:
305 count
= HAMMER2_SET_COUNT
;
307 case HAMMER2_BREF_TYPE_INDIRECT
:
308 count
= blocksize(base
) / sizeof(hammer2_blockref_t
);
316 * Find the best candidate (the lowest blockref within the specified
317 * range). The array can be fully set associative so make no ordering
321 best
.key
= HAMMER2_KEY_MAX
;
324 for (i
= 0; i
< count
; ++i
) {
326 * [re]load when returning from our recursion
328 if (base
->type
!= HAMMER2_BREF_TYPE_VOLUME
&&
329 base
->data_off
!= saved_base
.data_off
) {
330 if (blocksize(base
) && h2read(hfs
, &media
,
340 * Special case embedded file data
342 if (base
->type
== HAMMER2_BREF_TYPE_INODE
) {
343 if (media
.ipdata
.meta
.op_flags
&
344 HAMMER2_OPFLAG_DIRECTDATA
) {
345 *pptr
= media
.ipdata
.u
.data
;
346 bref_ret
->type
= HAMMER2_BREF_TYPE_DATA
;
348 return HAMMER2_EMBEDDED_BYTES
;
353 * Calculate the bref in our scan.
356 case HAMMER2_BREF_TYPE_VOLUME
:
357 bref
= &hfs
->sroot_blockset
.blockref
[i
];
359 case HAMMER2_BREF_TYPE_INODE
:
360 bref
= &media
.ipdata
.u
.blockset
.blockref
[i
];
362 case HAMMER2_BREF_TYPE_INDIRECT
:
363 bref
= &media
.npdata
[i
];
368 if (bref
->key
> best
.key
)
370 scan_beg
= bref
->key
;
371 scan_end
= scan_beg
+ ((hammer2_key_t
)1 << bref
->keybits
) - 1;
372 if (scan_end
>= key_beg
&& scan_beg
<= key_end
) {
378 * Figure out what to do with the results.
388 case HAMMER2_BREF_TYPE_INDIRECT
:
390 * Matched an indirect block. If the block turns out to
391 * contain nothing we continue the iteration, otherwise
392 * we return the data from the recursion.
394 * Be sure to handle the overflow case when recalculating
397 rc
= h2lookup(hfs
, &best
, key_beg
, key_end
, bref_ret
, pptr
);
400 ((hammer2_key_t
)1 << best
.keybits
);
401 if (key_beg
> best
.key
&& key_beg
<= key_end
)
405 case HAMMER2_BREF_TYPE_DIRENT
:
406 case HAMMER2_BREF_TYPE_INODE
:
407 case HAMMER2_BREF_TYPE_DATA
:
409 * Terminal match. Leaf elements might not be data-aligned.
411 dev_bsize
= blocksize(&best
);
413 if (dev_bsize
< HAMMER2_LBUFSIZE
)
414 dev_bsize
= HAMMER2_LBUFSIZE
;
415 dev_boff
= blockoff(&best
) -
416 (blockoff(&best
) & ~HAMMER2_LBUFMASK64
);
417 if (h2read(hfs
, &media
,
419 blockoff(&best
) - dev_boff
)) {
423 saved_base
.data_off
= (hammer2_off_t
)-1;
425 *pptr
= media
.buf
+ dev_boff
;
426 rc
= blocksize(&best
);
434 h2resolve(struct hammer2_fs
*hfs
, const char *path
,
435 hammer2_blockref_t
*bref
, hammer2_inode_data_t
**inop
)
437 hammer2_blockref_t bres
;
438 hammer2_inode_data_t
*ino
;
445 * Start point (superroot)
453 * Iterate path elements
458 if (*path
== 0) /* terminal */
462 * Calculate path element and look for it in the directory
464 for (len
= 0; path
[len
]; ++len
) {
465 if (path
[len
] == '/')
468 key
= hammer2_dirhash(path
, len
);
470 bytes
= h2lookup(hfs
, bref
,
472 key
| HAMMER2_DIRHASH_LOMASK
,
473 &bres
, (void **)&data
);
479 case HAMMER2_BREF_TYPE_DIRENT
:
480 if (bres
.embed
.dirent
.namlen
!= len
)
482 if (bres
.embed
.dirent
.namlen
<=
483 sizeof(bres
.check
.buf
)) {
484 if (memcmp(path
, bres
.check
.buf
, len
))
487 if (memcmp(path
, data
, len
))
492 * Found, resolve inode. This will set
493 * ino similarly to HAMMER2_BREF_TYPE_INODE
494 * and adjust bres, which path continuation
498 bytes
= h2lookup(hfs
, bref
,
499 bres
.embed
.dirent
.inum
,
500 bres
.embed
.dirent
.inum
,
501 &bres
, (void **)&ino
);
505 break; /* NOT REACHED */
506 case HAMMER2_BREF_TYPE_INODE
:
508 if (ino
->meta
.name_len
!= len
)
510 if (memcmp(path
, ino
->filename
, len
) == 0) {
517 if ((bres
.key
& 0xFFFF) == 0xFFFF) {
528 if (bytes
< 0 || bres
.type
== 0) {
529 bref
->data_off
= (hammer2_off_t
)-1;
535 * Check path continuance, inode must be a directory or
539 if (*path
&& ino
->meta
.type
!= HAMMER2_OBJTYPE_DIRECTORY
) {
540 bref
->data_off
= (hammer2_off_t
)-1;
549 h2readfile(struct hammer2_fs
*hfs
, hammer2_blockref_t
*bref
,
550 off_t off
, off_t filesize
, void *buf
, size_t len
)
552 hammer2_blockref_t bres
;
563 if (off
+ len
> filesize
)
564 len
= filesize
- off
;
572 * Find closest bres >= requested offset.
574 bytes
= h2lookup(hfs
, bref
, off
, off
+ len
- 1,
575 &bres
, (void **)&data
);
584 * Load the data into the buffer. First handle a degenerate
594 * Returned record overlaps to the left of the requested
595 * position. It must overlap in this case or h2lookup()
596 * would have returned something else.
598 if (bres
.key
< off
) {
599 data
+= off
- bres
.key
;
600 bytes
-= off
- bres
.key
;
604 * Returned record overlaps to the right of the requested
605 * position, handle zero-fill. Again h2lookup() only returns
606 * this case if there is an actual overlap.
608 if (bres
.key
> off
) {
609 zfill
= (ssize_t
)(bres
.key
- off
);
614 buf
= (char *)buf
+ zfill
;
618 * Trim returned request before copying.
622 bcopy(data
, buf
, bytes
);
626 buf
= (char *)buf
+ bytes
;
633 h2init(struct hammer2_fs
*hfs
)
638 hammer2_tid_t best_tid
= 0;
646 * Find the best volume header.
648 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
649 * given a disk offset beyond the EOM. XXX We need to probe the
650 * size of the media and limit our accesses, until then we have
651 * to give up if the first volume header does not have a hammer2
654 * XXX Probably still going to be problems w/ HAMMER2 volumes on
655 * media which is too small w/certain BIOSes.
658 for (i
= 0; i
< HAMMER2_NUM_VOLHDRS
; ++i
) {
659 off
= i
* HAMMER2_ZONE_BYTES64
;
662 if (h2read(hfs
, &media
, sizeof(media
.voldata
), off
))
664 if (media
.voldata
.magic
!= HAMMER2_VOLUME_ID_HBO
)
666 if (best
< 0 || best_tid
< media
.voldata
.mirror_tid
) {
668 best_tid
= media
.voldata
.mirror_tid
;
676 * Reload the best volume header and set up the blockref.
677 * We messed with media, clear the cache before continuing.
679 off
= best
* HAMMER2_ZONE_BYTES64
;
680 if (h2read(hfs
, &media
, sizeof(media
.voldata
), off
))
682 hfs
->sroot
.type
= HAMMER2_BREF_TYPE_VOLUME
;
683 hfs
->sroot
.data_off
= off
;
684 hfs
->sroot_blockset
= media
.voldata
.sroot_blockset
;
685 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
688 * Lookup sroot/BOOT and clear the cache again.
690 r
= h2lookup(hfs
, &hfs
->sroot
,
691 HAMMER2_SROOT_KEY
, HAMMER2_SROOT_KEY
,
695 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
696 r
= h2lookup(hfs
, &hfs
->sroot
,
698 HAMMER2_BOOT_KEY
| HAMMER2_DIRHASH_LOMASK
,
701 printf("hammer2: 'BOOT' PFS not found\n");
704 h2lookup(hfs
, NULL
, 0, 0, NULL
, NULL
);
709 /************************************************************************
711 ************************************************************************
716 static struct hammer2_fs hfs
;
719 boot2_hammer2_init(void)
727 boot2_hammer2_lookup(const char *path
)
729 hammer2_blockref_t bref
;
731 h2resolve(&hfs
, path
, &bref
, NULL
);
732 return ((boot2_ino_t
)bref
.data_off
);
736 boot2_hammer2_read(boot2_ino_t ino
, void *buf
, size_t len
)
738 hammer2_blockref_t bref
;
741 bzero(&bref
, sizeof(bref
));
742 bref
.type
= HAMMER2_BREF_TYPE_INODE
;
745 total
= h2readfile(&hfs
, &bref
, fs_off
, 0x7FFFFFFF, buf
, len
);
751 const struct boot2_fsapi boot2_hammer2_api
= {
752 .fsinit
= boot2_hammer2_init
,
753 .fslookup
= boot2_hammer2_lookup
,
754 .fsread
= boot2_hammer2_read
759 /************************************************************************
761 ************************************************************************
767 struct hammer2_fs hfs
;
768 hammer2_blockref_t bref
;
776 hammer2_get_dtype(uint8_t type
)
779 case HAMMER2_OBJTYPE_DIRECTORY
:
781 case HAMMER2_OBJTYPE_REGFILE
:
783 case HAMMER2_OBJTYPE_FIFO
:
785 case HAMMER2_OBJTYPE_CDEV
:
787 case HAMMER2_OBJTYPE_BDEV
:
789 case HAMMER2_OBJTYPE_SOFTLINK
:
791 case HAMMER2_OBJTYPE_SOCKET
:
800 hammer2_get_mode(uint8_t type
)
803 case HAMMER2_OBJTYPE_DIRECTORY
:
805 case HAMMER2_OBJTYPE_REGFILE
:
807 case HAMMER2_OBJTYPE_FIFO
:
809 case HAMMER2_OBJTYPE_CDEV
:
811 case HAMMER2_OBJTYPE_BDEV
:
813 case HAMMER2_OBJTYPE_SOFTLINK
:
815 case HAMMER2_OBJTYPE_SOCKET
:
823 hammer2_open(const char *path
, struct open_file
*f
)
825 struct hfile
*hf
= malloc(sizeof(*hf
));
826 hammer2_inode_data_t
*ipdata
;
828 bzero(hf
, sizeof(*hf
));
833 if (h2init(&hf
->hfs
)) {
839 h2resolve(&hf
->hfs
, path
, &hf
->bref
, &ipdata
);
840 if (hf
->bref
.data_off
== (hammer2_off_t
)-1 ||
841 (hf
->bref
.type
!= HAMMER2_BREF_TYPE_INODE
&&
842 hf
->bref
.type
!= HAMMER2_BREF_TYPE_VOLUME
)) {
849 hf
->fsize
= ipdata
->meta
.size
;
850 hf
->type
= ipdata
->meta
.type
;
851 hf
->mode
= ipdata
->meta
.mode
|
852 hammer2_get_mode(ipdata
->meta
.type
);
855 hf
->type
= HAMMER2_OBJTYPE_DIRECTORY
;
856 hf
->mode
= 0755 | S_IFDIR
;
862 hammer2_close(struct open_file
*f
)
864 struct hfile
*hf
= f
->f_fsdata
;
873 hammer2_read(struct open_file
*f
, void *buf
, size_t len
, size_t *resid
)
875 struct hfile
*hf
= f
->f_fsdata
;
879 total
= h2readfile(&hf
->hfs
, &hf
->bref
,
880 f
->f_offset
, hf
->fsize
, buf
, len
);
885 f
->f_offset
+= total
;
888 *resid
= len
- total
;
893 hammer2_seek(struct open_file
*f
, off_t offset
, int whence
)
895 struct hfile
*hf
= f
->f_fsdata
;
899 f
->f_offset
= offset
;
902 f
->f_offset
+= offset
;
905 f
->f_offset
= hf
->fsize
- offset
;
910 return (f
->f_offset
);
914 hammer2_stat(struct open_file
*f
, struct stat
*st
)
916 struct hfile
*hf
= f
->f_fsdata
;
918 st
->st_mode
= hf
->mode
;
921 st
->st_size
= hf
->fsize
;
927 hammer2_readdir(struct open_file
*f
, struct dirent
*den
)
929 struct hfile
*hf
= f
->f_fsdata
;
930 hammer2_blockref_t bres
;
931 hammer2_inode_data_t
*ipdata
;
936 bytes
= h2lookup(&hf
->hfs
, &hf
->bref
,
937 f
->f_offset
| HAMMER2_DIRHASH_VISIBLE
,
939 &bres
, (void **)&data
);
943 case HAMMER2_BREF_TYPE_INODE
:
945 den
->d_namlen
= ipdata
->meta
.name_len
;
946 den
->d_type
= hammer2_get_dtype(ipdata
->meta
.type
);
947 den
->d_ino
= ipdata
->meta
.inum
;
948 bcopy(ipdata
->filename
, den
->d_name
, den
->d_namlen
);
949 den
->d_name
[den
->d_namlen
] = 0;
951 case HAMMER2_BREF_TYPE_DIRENT
:
952 den
->d_namlen
= bres
.embed
.dirent
.namlen
;
953 den
->d_type
= hammer2_get_dtype(bres
.embed
.dirent
.type
);
954 den
->d_ino
= bres
.embed
.dirent
.inum
;
955 if (den
->d_namlen
<= sizeof(bres
.check
.buf
)) {
956 bcopy(bres
.check
.buf
,
960 bcopy(data
, den
->d_name
, den
->d_namlen
);
962 den
->d_name
[den
->d_namlen
] = 0;
967 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE
);
968 den
->d_name
[0] = '?';
973 f
->f_offset
= bres
.key
+ 1;
980 struct fs_ops hammer2_fsops
= {